home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1998 September / PCO_0998.ISO / filesbbs / dos / sbbs_src.exe / SBBS / MSG2.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-09-05  |  34.6 KB  |  1,203 lines

  1. #line 1 "MSG2.C"
  2.  
  3. /* Developed 1990-1997 by Rob Swindell; PO Box 501, Yorba Linda, CA 92885 */
  4.  
  5. #include "sbbs.h"
  6.  
  7. #define MAX_LINE_LEN 82L
  8.  
  9. char *qstr=" > %.76s\r\n";
  10.  
  11. int qwk_route(char *inaddr, char *fulladdr);
  12.  
  13. void remove_re(char *str)
  14. {
  15. while(!strnicmp(str,"RE:",3)) {
  16.     strcpy(str,str+3);
  17.     while(str[0]==SP)
  18.         strcpy(str,str+1); }
  19. }
  20.  
  21. /****************************************************************************/
  22. /* Modify 'str' to for quoted format. Remove ^A codes, etc.                 */
  23. /****************************************************************************/
  24. void quotestr(char *str)
  25. {
  26.     char tmp[512];
  27.     int i,j;
  28.  
  29. j=strlen(str);
  30. while(j && (str[j-1]==SP || str[j-1]==LF || str[j-1]==CR)) j--;
  31. str[j]=0;
  32. remove_ctrl_a(str);
  33. }
  34.  
  35. void editor_inf(int xeditnum,char *dest, char *title,int mode
  36.     ,uint subnum)
  37. {
  38.     char str[512];
  39.     int file;
  40.  
  41. xeditnum--;
  42.  
  43. if(xedit[xeditnum]->misc&QUICKBBS) {
  44.     sprintf(str,"%sMSGINF",node_dir);
  45.     if((file=nopen(str,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
  46.         errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC);
  47.         return; }
  48.     sprintf(str,"%s\r\n%s\r\n%s\r\n%u\r\n%s\r\n%s\r\n"
  49.         ,(subnum!=INVALID_SUB && sub[subnum]->misc&SUB_NAME) ? useron.name
  50.             : useron.alias
  51.             ,dest,title,1
  52.             ,mode&WM_NETMAIL ? "NetMail"
  53.             :mode&WM_EMAIL ? "Electronic Mail"
  54.             :subnum==INVALID_SUB ? nulstr
  55.             :sub[subnum]->sname
  56.         ,mode&WM_PRIVATE ? "YES":"NO");
  57.     write(file,str,strlen(str));
  58.     close(file); }
  59. else {
  60.     sprintf(str,"%sEDITOR.INF",node_dir);
  61.     if((file=nopen(str,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
  62.         errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC);
  63.         return; }
  64.     sprintf(str,"%s\r\n%s\r\n%u\r\n%s\r\n%s\r\n%u\r\n"
  65.         ,title,dest,useron.number
  66.         ,(subnum!=INVALID_SUB && sub[subnum]->misc&SUB_NAME) ? useron.name
  67.         : useron.alias
  68.         ,useron.name,useron.level);
  69.     write(file,str,strlen(str));
  70.     close(file); }
  71. }
  72.  
  73. /****************************************************************************/
  74. /* Creates a message (post or mail) using standard line editor. 'fname' is  */
  75. /* is name of file to create, 'top' is a buffer to place at beginning of    */
  76. /* message and 'title' is the title (70chars max) for the message.          */
  77. /* 'dest' contains a text description of where the message is going.        */
  78. /* Returns positive if successful or 0 if aborted.                          */
  79. /****************************************************************************/
  80. char writemsg(char *fname, char *top, char *title, int mode, int subnum
  81.     ,char *dest)
  82. {
  83.     uchar str[256],quote[128],msgtmp[32],c,HUGE16 *buf,ex_mode=0,*p,*tp
  84.         ,useron_level;
  85.     int i,j,file,linesquoted=0;
  86.     long length,qlen,qtime;
  87.     ulong l;
  88.     FILE *stream;
  89.  
  90. useron_level=useron.level;
  91.  
  92. if((buf=(char HUGE16*)LMALLOC(level_linespermsg[useron_level]*MAX_LINE_LEN))
  93.     ==NULL) {
  94.     errormsg(WHERE,ERR_ALLOC,fname
  95.         ,level_linespermsg[useron_level]*MAX_LINE_LEN);
  96.     return(0); }
  97.  
  98. if(mode&WM_NETMAIL ||
  99.     (!(mode&(WM_EMAIL|WM_NETMAIL)) && sub[subnum]->misc&SUB_PNET))
  100.     mode|=WM_NOTOP;
  101.  
  102. if(useron.xedit && xedit[useron.xedit-1]->misc&QUICKBBS)
  103.     strcpy(msgtmp,"MSGTMP");
  104. else
  105.     strcpy(msgtmp,"INPUT.MSG");
  106.  
  107. if(mode&WM_QUOTE && !(useron.rest&FLAG('J'))
  108.     && ((mode&(WM_EMAIL|WM_NETMAIL) && sys_misc&SM_QUOTE_EM)
  109.     || (!(mode&(WM_EMAIL|WM_NETMAIL)) && (uint)subnum!=INVALID_SUB
  110.         && sub[subnum]->misc&SUB_QUOTE))) {
  111.  
  112.     /* Quote entire message to MSGTMP or INPUT.MSG */
  113.  
  114.     if(useron.xedit && xedit[useron.xedit-1]->misc"EALL) {
  115.         sprintf(str,"%sQUOTES.TXT",node_dir);
  116.         if((stream=fnopen(&file,str,O_RDONLY))==NULL) {
  117.             errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
  118.             LFREE(buf);
  119.             return(0); }
  120.  
  121.         sprintf(str,"%s%s",node_dir,msgtmp);    /* file for quoted msg */
  122.         if((file=nopen(str,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
  123.             errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC);
  124.             LFREE(buf);
  125.             fclose(stream);
  126.             return(0); }
  127.  
  128.         while(!feof(stream) && !ferror(stream)) {
  129.             if(!fgets(str,255,stream))
  130.                 break;
  131.             quotestr(str);
  132.             sprintf(tmp,qstr,str);
  133.             write(file,tmp,strlen(tmp));
  134.             linesquoted++; }
  135.         fclose(stream);
  136.         close(file); }
  137.  
  138.     /* Quote nothing to MSGTMP or INPUT.MSG automatically */
  139.  
  140.     else if(useron.xedit && xedit[useron.xedit-1]->misc"ENONE)
  141.         ;
  142.  
  143.     else if(yesno(text[QuoteMessageQ])) {
  144.         sprintf(str,"%sQUOTES.TXT",node_dir);
  145.         if((stream=fnopen(&file,str,O_RDONLY))==NULL) {
  146.             errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
  147.             LFREE(buf);
  148.             return(0); }
  149.  
  150.         sprintf(str,"%s%s",node_dir,msgtmp);    /* file for quoted msg */
  151.         if((file=nopen(str,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
  152.             errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC);
  153.             LFREE(buf);
  154.             fclose(stream);
  155.             return(0); }
  156.  
  157.         l=ftell(stream);            /* l now points to start of message */
  158.  
  159.         while(online) {
  160.             sprintf(str,text[QuoteLinesPrompt],linesquoted ? "Done":"All");
  161.             mnemonics(str);
  162.             i=getstr(quote,10,K_UPPER);
  163.             if(sys_status&SS_ABORT) {
  164.                 fclose(stream);
  165.                 close(file);
  166.                 LFREE(buf);
  167.                 return(0); }
  168.             if(!i && linesquoted)
  169.                 break;
  170.             if(!i || quote[0]=='A') {                   /* Quote all */
  171.                 fseek(stream,l,SEEK_SET);
  172.                 while(!feof(stream) && !ferror(stream)) {
  173.                     if(!fgets(str,255,stream))
  174.                         break;
  175.                     quotestr(str);
  176.                     sprintf(tmp,qstr,str);
  177.                     write(file,tmp,strlen(tmp));
  178.                     linesquoted++; }
  179.                 break; }
  180.             if(quote[0]=='L') {
  181.                 fseek(stream,l,SEEK_SET);
  182.                 i=1;
  183.                 CRLF;
  184.                 attr(LIGHTGRAY);
  185.                 while(!feof(stream) && !ferror(stream) && !msgabort()) {
  186.                     if(!fgets(str,255,stream))
  187.                         break;
  188.                     quotestr(str);
  189.                     bprintf("%3d: %.74s\r\n",i,str);
  190.                     i++; }
  191.                 continue; }
  192.  
  193.             if(!isdigit(quote[0]))
  194.                 break;
  195.             p=quote;
  196.             while(p) {
  197.                 if(*p==',' || *p==SP)
  198.                     p++;
  199.                 i=atoi(p);
  200.                 if(!i)
  201.                     break;
  202.                 fseek(stream,l,SEEK_SET);
  203.                 j=1;
  204.                 while(!feof(stream) && !ferror(stream) && j<i) {
  205.                     if(!fgets(tmp,255,stream))
  206.                         break;
  207.                     j++; }        /* skip beginning */
  208.                 tp=strchr(p,'-');   /* tp for temp pointer */
  209.                 if(tp) {         /* range */
  210.                     i=atoi(tp+1);
  211.                     while(!feof(stream) && !ferror(stream) && j<=i) {
  212.                         if(!fgets(str,255,stream))
  213.                             break;
  214.                         quotestr(str);
  215.                         sprintf(tmp,qstr,str);
  216.                         write(file,tmp,strlen(tmp));
  217.                         linesquoted++;
  218.                         j++; } }
  219.                 else {            /* one line */
  220.                     if(fgets(str,255,stream)) {
  221.                         quotestr(str);
  222.                         sprintf(tmp,qstr,str);
  223.                         write(file,tmp,strlen(tmp));
  224.                         linesquoted++; } }
  225.                 p=strchr(p,',');
  226.                 // if(!p) p=strchr(p,SP);  02/05/96 huh?
  227.                 } }
  228.  
  229.         fclose(stream);
  230.         close(file); } }
  231. else {
  232.     sprintf(str,"%sQUOTES.TXT",node_dir);
  233.     remove(str); }
  234.  
  235. if(!online || sys_status&SS_ABORT) {
  236.     LFREE(buf);
  237.     return(0); }
  238.  
  239. if(!(mode&WM_EXTDESC)) {
  240.     if(mode&WM_FILE) {
  241.         c=12;
  242.         CRLF;
  243.         bputs(text[Filename]); }
  244.     else {
  245.         c=LEN_TITLE;
  246.         bputs(text[TitlePrompt]); }
  247.     if(!(mode&(WM_EMAIL|WM_NETMAIL)) && !(mode&WM_FILE)
  248.         && sub[subnum]->misc&(SUB_QNET /* |SUB_PNET */ ))
  249.         c=25;
  250.     if(mode&WM_QWKNET)
  251.         c=25;
  252.     if(!getstr(title,c,mode&WM_FILE ? K_LINE|K_UPPER : K_LINE|K_EDIT|K_AUTODEL)
  253.         && useron_level && useron.logons) {
  254.         LFREE(buf);
  255.         return(0); }
  256.     if(!(mode&(WM_EMAIL|WM_NETMAIL)) && sub[subnum]->misc&SUB_QNET
  257.         && !SYSOP
  258.         && (!stricmp(title,"DROP") || !stricmp(title,"ADD")
  259.         || !strnicmp(dest,"SBBS",4))) {
  260.         LFREE(buf);   /* Users can't post DROP or ADD in QWK netted subs */
  261.         return(0); } }    /* or messages to "SBBS" */
  262.  
  263. if(!online || sys_status&SS_ABORT) {
  264.     LFREE(buf);
  265.     return(0); }
  266.  
  267. /* Create WWIV compatible EDITOR.INF file */
  268.  
  269. if(useron.xedit) {
  270.     editor_inf(useron.xedit,dest,title,mode,subnum);
  271.     if(xedit[useron.xedit-1]->type) {
  272.         gettimeleft();
  273.         xtrndat(useron.alias,node_dir,xedit[useron.xedit-1]->type,timeleft); }
  274.     }
  275.  
  276. if(console&CON_RAW_IN) {
  277.     bprintf(text[EnterMsgNowRaw]
  278.         ,(ulong)level_linespermsg[useron_level]*MAX_LINE_LEN);
  279.     if(top[0] && !(mode&WM_NOTOP)) {
  280.         strcpy((char *)buf,top);
  281.         strcat((char *)buf,crlf);
  282.         l=strlen((char *)buf); }
  283.     else
  284.         l=0;
  285.     while(l<level_linespermsg[useron_level]*MAX_LINE_LEN) {
  286.         c=getkey(0);
  287.         if(sys_status&SS_ABORT) {  /* Ctrl-C */
  288.             LFREE(buf);
  289.             return(0); }
  290.         if((c==ESC || c==1) && useron.rest&FLAG('A')) /* ANSI restriction */
  291.             continue;
  292. #ifndef __OS2__
  293.         if(lclaes() && c=='"') c=7; /* convert keyboard reassignmnet to beep */
  294. #endif
  295.         if(c==7 && useron.rest&FLAG('B'))   /* Beep restriction */
  296.             continue;
  297.         if(!(console&CON_RAW_IN))    /* Ctrl-Z was hit */
  298.             break;
  299.         outchar(c);
  300.         buf[l++]=c; }
  301.     buf[l]=0;
  302.     if(l==level_linespermsg[useron_level]*MAX_LINE_LEN)
  303.         bputs(text[OutOfBytes]); }
  304.  
  305.  
  306. else if((online==ON_LOCAL && node_misc&NM_LCL_EDIT && node_editor[0])
  307.     || useron.xedit) {
  308.  
  309.     if(useron.xedit && xedit[useron.xedit-1]->misc&IO_INTS) {
  310.         ex_mode|=EX_OUTL;
  311.         if(online==ON_REMOTE)
  312.             ex_mode|=(EX_OUTR|EX_INR);
  313.         if(xedit[useron.xedit-1]->misc&WWIVCOLOR)
  314.             ex_mode|=EX_WWIV; }
  315.  
  316.     sprintf(str,"%s%s",node_dir,msgtmp);    /* temporary file for input */
  317.     if(!linesquoted && fexist(str))
  318.         remove(str);
  319.     if(linesquoted) {
  320.         qlen=flength(str);
  321.         qtime=fdate(str); }
  322.     if(online==ON_LOCAL) {
  323.         if(node_misc&NM_LCL_EDIT && node_editor[0])
  324.             external(cmdstr(node_editor,str,nulstr,NULL)
  325.                 ,0);  /* EX_CC removed and EX_OUTL removed */
  326.         else
  327.             external(cmdstr(xedit[useron.xedit-1]->lcmd,str,nulstr,NULL)
  328.                 ,ex_mode); }
  329.  
  330.     else {
  331.         rioctl(IOCM|PAUSE|ABORT);
  332.         external(cmdstr(xedit[useron.xedit-1]->rcmd,str,nulstr,NULL),ex_mode);
  333.         rioctl(IOSM|PAUSE|ABORT); }
  334.     checkline();
  335.     if(!fexist(str) || !online
  336.         || (linesquoted && qlen==flength(str) && qtime==fdate(str))) {
  337.         LFREE(buf);
  338.         return(0); }
  339.     buf[0]=0;
  340.     if(!(mode&WM_NOTOP))
  341.         strcpy((char *)buf,top);
  342.     if((file=nopen(str,O_RDONLY))==-1) {
  343.         errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
  344.         LFREE(buf);
  345.         return(0); }
  346.     length=filelength(file);
  347.     l=strlen((char *)buf);      /* reserve space for top and terminating null */
  348.     /* truncate if too big */
  349.     if(length>(level_linespermsg[useron_level]*MAX_LINE_LEN)-(l+1)) {
  350.         length=(level_linespermsg[useron_level]*MAX_LINE_LEN)-(l+1);
  351.         bputs(text[OutOfBytes]); }
  352.     lread(file,buf+l,length);
  353.     close(file);
  354.     // remove(str);        /* no need to save the temp input file */
  355.     buf[l+length]=0; }
  356. else {
  357.     buf[0]=0;
  358.     if(linesquoted) {
  359.         sprintf(str,"%s%s",node_dir,msgtmp);
  360.         if((file=nopen(str,O_RDONLY))!=-1) {
  361.             length=filelength(file);
  362.             l=length>level_linespermsg[useron_level]*MAX_LINE_LEN
  363.                 ? level_linespermsg[useron_level]*MAX_LINE_LEN : length;
  364.             lread(file,buf,l);
  365.             buf[l]=0;
  366.             close(file);
  367.             // remove(str);
  368.             } }
  369.     if(!(msgeditor((char *)buf,mode&WM_NOTOP ? nulstr : top,title))) {
  370.         LFREE(buf);
  371.         return(0); } }
  372.  
  373. now=time(NULL);
  374. bputs(text[Saving]);
  375. if((stream=fnopen(&file,fname,O_WRONLY|O_CREAT|O_TRUNC))==NULL) {
  376.     errormsg(WHERE,ERR_OPEN,fname,O_WRONLY|O_CREAT|O_TRUNC);
  377.     LFREE(buf);
  378.     return(0); }
  379. for(l=i=0;buf[l] && i<level_linespermsg[useron_level];l++) {
  380.     if(buf[l]==141 && useron.xedit && xedit[useron.xedit-1]->misc&QUICKBBS) {
  381.         fwrite(crlf,2,1,stream);
  382.         i++;
  383.         continue; }
  384.     if(buf[l]==LF && (!l || buf[l-1]!=CR) && useron.xedit
  385.         && xedit[useron.xedit-1]->misc&EXPANDLF) {
  386.         fwrite(crlf,2,1,stream);
  387.         i++;
  388.         continue; }
  389.     if(!(mode&(WM_EMAIL|WM_NETMAIL))
  390.         && (!l || buf[l-1]==LF)
  391.         && buf[l]=='-' && buf[l+1]=='-' && buf[l+2]=='-'
  392.         && (buf[l+3]==SP || buf[l+3]==TAB || buf[l+3]==CR))
  393.         buf[l+1]='+';
  394.     if(buf[l]==LF)
  395.         i++;
  396.     fputc(buf[l],stream); }
  397.  
  398. if(buf[l])
  399.     bputs(text[NoMoreLines]);
  400. fclose(stream);
  401. LFREE((char *)buf);
  402. bprintf(text[SavedNBytes],l,i);
  403. return(1);
  404. }
  405.  
  406.  
  407. /****************************************************************************/
  408. /* Removes from file 'str' every LF terminated line that starts with 'str2' */
  409. /* That is divisable by num. Function skips first 'skip' number of lines    */
  410. /****************************************************************************/
  411. void removeline(char *str, char *str2, char num, char skip)
  412. {
  413.     char    HUGE16 *buf;
  414.     char    slen,c;
  415.     int     i,file;
  416.     ulong    l=0,m,flen;
  417.     FILE    *stream;
  418.  
  419. if((file=nopen(str,O_RDONLY))==-1) {
  420.     errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
  421.     return; }
  422. flen=filelength(file);
  423. slen=strlen(str2);
  424. if((buf=(char *)MALLOC(flen))==NULL) {
  425.     close(file);
  426.     errormsg(WHERE,ERR_ALLOC,str,flen);
  427.     return; }
  428. if(lread(file,buf,flen)!=flen) {
  429.     close(file);
  430.     errormsg(WHERE,ERR_READ,str,flen);
  431.     FREE(buf);
  432.     return; }
  433. close(file);
  434. if((stream=fnopen(&file,str,O_WRONLY|O_TRUNC))==NULL) {
  435.     close(file);
  436.     errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_TRUNC);
  437.     FREE(buf);
  438.     return; }
  439. for(i=0;l<flen && i<skip;l++) {
  440.     fputc(buf[l],stream);
  441.     if(buf[l]==LF)
  442.         i++; }
  443. while(l<flen) {
  444.     if(!strncmp((char *)buf+l,str2,slen)) {
  445.         for(i=0;i<num && l<flen;i++) {
  446.             while(l<flen && buf[l]!=LF) l++;
  447.             l++; } }
  448.     else {
  449.         for(i=0;i<num && l<flen;i++) {
  450.             while(l<flen && buf[l]!=LF) fputc(buf[l++],stream);
  451.             fputc(buf[l++],stream); } } }
  452. fclose(stream);
  453. FREE((char *)buf);
  454. }
  455.  
  456. /*****************************************************************************/
  457. /* The Synchronet editor.                                                    */
  458. /* Returns the number of lines edited.                                       */
  459. /*****************************************************************************/
  460. ulong msgeditor(char *buf, char *top, char *title)
  461. {
  462.     int i,j,line,lines=0,maxlines;
  463.     char strin[256],**str,done=0;
  464.     ulong l,m;
  465.  
  466. if(online==ON_REMOTE) {
  467.     rioctl(IOCM|ABORT);
  468.     rioctl(IOCS|ABORT); }
  469.  
  470. maxlines=level_linespermsg[useron.level];
  471.  
  472. if((str=(char **)MALLOC(sizeof(char *)*(maxlines+1)))==NULL) {
  473.     errormsg(WHERE,ERR_ALLOC,"msgeditor",sizeof(char *)*(maxlines+1));
  474.     return(0); }
  475. m=strlen(buf);
  476. l=0;
  477. while(l<m && lines<maxlines) {
  478.     msgabort(); /* to allow pausing */
  479.     if((str[lines]=MALLOC(MAX_LINE_LEN))==NULL) {
  480.         errormsg(WHERE,ERR_ALLOC,nulstr,MAX_LINE_LEN);
  481.         for(i=0;i<lines;i++)
  482.             FREE(str[lines]);
  483.         FREE(str);
  484.         if(online==ON_REMOTE)
  485.             rioctl(IOSM|ABORT);
  486.         return(0); }
  487.     for(i=0;i<79 && l<m;i++,l++) {
  488.         if(buf[l]==CR) {
  489.             l+=2;
  490.             break; }
  491.         if(buf[l]==TAB) {
  492.             if(!(i%8))                  /* hard-coded tabstop of 8 */
  493.                 str[lines][i++]=SP;     /* for expansion */
  494.             while(i%8 && i<79)
  495.                 str[lines][i++]=SP;
  496.             i--;
  497.             /***
  498.             bprintf("\r\nMessage editor: Expanded tab on line #%d",lines+1);
  499.             ***/ }
  500.         else str[lines][i]=buf[l]; }
  501.     if(i==79) {
  502.         if(buf[l]==CR)
  503.             l+=2;
  504.         else
  505.             bprintf("\r\nMessage editor: Split line #%d",lines+1); }
  506.     str[lines][i]=0;
  507.     lines++; }
  508. if(lines)
  509.     bprintf("\r\nMessage editor: Read in %d lines\r\n",lines);
  510. bprintf(text[EnterMsgNow],maxlines);
  511. for(i=0;i<79;i++)
  512.     if(i%TABSIZE || !i)
  513.         outchar('-');
  514.     else outchar('+');
  515. CRLF;
  516. putmsg(top,P_SAVEATR|P_NOATCODES);
  517. for(line=0;line<lines && !msgabort();line++) { /* display lines in buf */
  518.     putmsg(str[line],P_SAVEATR|P_NOATCODES);
  519.     if(useron.misc&ANSI)
  520.         bputs("\x1b[K");  /* delete to end of line */
  521.     CRLF; }
  522. SYNC;
  523. if(online==ON_REMOTE)
  524.     rioctl(IOSM|ABORT);
  525. while(online && !done) {
  526.     checkline();
  527.     if(line==lines) {
  528.         if((str[line]=MALLOC(MAX_LINE_LEN))==NULL) {
  529.             errormsg(WHERE,ERR_ALLOC,nulstr,MAX_LINE_LEN);
  530.             for(i=0;i<lines;i++)
  531.                 FREE(str[i]);
  532.             FREE(str);
  533.             return(0); }
  534.         str[line][0]=0; }
  535.     if(line>(maxlines-10)) {
  536.         if(line==maxlines)
  537.             bputs(text[NoMoreLines]);
  538.         else
  539.             bprintf(text[OnlyNLinesLeft],maxlines-line); }
  540.     strcpy(strin,str[line]);
  541.     do {
  542.         if(!line)
  543.             outchar(CR);
  544.         getstr(strin,79,K_WRAP|K_MSG|K_EDIT);
  545.         } while(console&CON_UPARROW && !line);
  546.  
  547.     if(sys_status&SS_ABORT) {
  548.         if(line==lines)
  549.             FREE(str[line]);
  550.         continue; }
  551.     if(strin[0]=='/' && strlen(strin)<8) {
  552.         if(!stricmp(strin,"/DEBUG") && SYSOP) {
  553.             if(line==lines)
  554.                 FREE(str[line]);
  555.             bprintf("\r\nline=%d lines=%d rows=%d\r\n",line,lines,rows);
  556.             continue; }
  557.         else if(!stricmp(strin,"/ABT")) {
  558.             if(line==lines)         /* delete a line */
  559.                 FREE(str[line]);
  560.             for(i=0;i<lines;i++)
  561.                 FREE(str[i]);
  562.             FREE(str);
  563.             return(0); }
  564.         else if(toupper(strin[1])=='D') {
  565.             if(line==lines)         /* delete a line */
  566.                 FREE(str[line]);
  567.             if(!lines)
  568.                 continue;
  569.             i=atoi(strin+2)-1;
  570.             if(i==-1)   /* /D means delete last line */
  571.                 i=lines-1;
  572.             if(i>=lines || i<0)
  573.                 bputs(text[InvalidLineNumber]);
  574.             else {
  575.                 FREE(str[i]);
  576.                 lines--;
  577.                 while(i<lines) {
  578.                     str[i]=str[i+1];
  579.                     i++; }
  580.                 if(line>lines)
  581.                     line=lines; }
  582.             continue; }
  583.         else if(toupper(strin[1])=='I') {
  584.             if(line==lines)         /* insert a line before number x */
  585.                 FREE(str[line]);
  586.             if(line==maxlines || !lines)
  587.                 continue;
  588.             i=atoi(strin+2)-1;
  589.             if(i==-1)
  590.                 i=lines-1;
  591.             if(i>=lines || i<0)
  592.                 bputs(text[InvalidLineNumber]);
  593.             else {
  594.                 for(line=lines;line>i;line--)   /* move the pointers */
  595.                     str[line]=str[line-1];
  596.                 if((str[i]=MALLOC(MAX_LINE_LEN))==NULL) {
  597.                     errormsg(WHERE,ERR_ALLOC,nulstr,MAX_LINE_LEN);
  598.                     for(i=0;i<lines;i++)
  599.                         FREE(str[i]);
  600.                     FREE(str);
  601.                     return(0); }
  602.                 str[i][0]=0;
  603.                 line=++lines; }
  604.             continue; }
  605.         else if(toupper(strin[1])=='E') {
  606.             if(line==lines)         /* edit a line */
  607.                 FREE(str[line]);
  608.             if(!lines)
  609.                 continue;
  610.             i=atoi(strin+2)-1;
  611.             j=K_MSG|K_EDIT; /* use j for the getstr mode */
  612.             if(i==-1) { /* /E means edit last line */
  613.                 i=lines-1;
  614.                 j|=K_WRAP; }    /* wrap when editing last line */
  615.             if(i>=lines || i<0)
  616.                 bputs(text[InvalidLineNumber]);
  617.             else
  618.                 getstr(str[i],79,j);
  619.             continue; }
  620.         else if(!stricmp(strin,"/CLR")) {
  621.             bputs(text[MsgCleared]);
  622.             if(line!=lines)
  623.                 lines--;
  624.             for(i=0;i<=lines;i++)
  625.                 FREE(str[i]);
  626.             line=0;
  627.             lines=0;
  628.             putmsg(top,P_SAVEATR|P_NOATCODES);
  629.             continue; }
  630.         else if(toupper(strin[1])=='L') {   /* list message */
  631.             if(line==lines)
  632.                 FREE(str[line]);
  633.             if(lines)
  634.                 i=!noyes(text[WithLineNumbers]);
  635.             CRLF;
  636.             attr(LIGHTGRAY);
  637.             putmsg(top,P_SAVEATR|P_NOATCODES);
  638.             if(!lines) {
  639.                 continue; }
  640.             j=atoi(strin+2);
  641.             if(j) j--;  /* start from line j */
  642.             while(j<lines && !msgabort()) {
  643.                 if(i) { /* line numbers */
  644.                     sprintf(tmp,"%3d: %-.74s",j+1,str[j]);
  645.                     putmsg(tmp,P_SAVEATR|P_NOATCODES); }
  646.                 else
  647.                     putmsg(str[j],P_SAVEATR|P_NOATCODES);
  648.                 if(useron.misc&ANSI)
  649.                     bputs("\x1b[K");  /* delete to end of line */
  650.                 CRLF;
  651.                 j++; }
  652.             SYNC;
  653.             continue; }
  654.         else if(!stricmp(strin,"/S")) { /* Save */
  655.             if(line==lines)
  656.                 FREE(str[line]);
  657.             done=1;
  658.             continue;}
  659.         else if(!stricmp(strin,"/T")) { /* Edit title */
  660.             if(line==lines)
  661.                 FREE(str[line]);
  662.             if(title[0]) {
  663.                 bputs(text[TitlePrompt]);
  664.                 getstr(title,LEN_TITLE,K_LINE|K_EDIT|K_AUTODEL);
  665.                 SYNC;
  666.                 CRLF; }
  667.             continue; }
  668.         else if(!stricmp(strin,"/?")) {
  669.             if(line==lines)
  670.                 FREE(str[line]);
  671.             menu("EDITOR"); /* User Editor Commands */
  672.             SYNC;
  673.             continue; }
  674.         else if(!stricmp(strin,"/ATTR"))    {
  675.             if(line==lines)
  676.                 FREE(str[line]);
  677.             menu("ATTR");   /* User ANSI Commands */
  678.             SYNC;
  679.             continue; } }
  680.     strcpy(str[line],strin);
  681.     if(line<maxlines)
  682.         line++;
  683.     else
  684.         FREE(str[line]);
  685.     if(line>lines)
  686.         lines++;
  687.     if(console&CON_UPARROW) {
  688.         outchar(CR);
  689.         bprintf("\x1b[A\x1b[K");    /* up one line, clear to eol */
  690.         line-=2; }
  691.     }
  692. if(!online) {
  693.     for(i=0;i<lines;i++)
  694.         FREE(str[i]);
  695.     FREE(str);
  696.     return(0); }
  697. strcpy(buf,top);
  698. for(i=0;i<lines;i++) {
  699.     strcat(buf,str[i]);
  700.     strcat(buf,crlf);
  701.     FREE(str[i]); }
  702. FREE(str);
  703. return(lines);
  704. }
  705.  
  706.  
  707. /****************************************************************************/
  708. /* Edits an existing file or creates a new one in MSG format                */
  709. /****************************************************************************/
  710. void editfile(char *str)
  711. {
  712.     char *buf,str2[128],mode=0;   /* EX_CC */
  713.     int file;
  714.     long length,maxlines,lines,l;
  715.  
  716. maxlines=level_linespermsg[useron.level];
  717. sprintf(str2,"%sQUOTES.TXT",node_dir);
  718. remove(str2);
  719. if(node_editor[0] && online==ON_LOCAL) {
  720.     external(cmdstr(node_editor,str,nulstr,NULL),0); /* EX_CC */
  721.     return; }
  722. if(useron.xedit) {
  723.     editor_inf(useron.xedit,nulstr,nulstr,0,INVALID_SUB);
  724.     if(xedit[useron.xedit-1]->misc&IO_INTS) {
  725.         mode|=EX_OUTL;
  726.         if(online==ON_REMOTE)
  727.             mode|=(EX_OUTR|EX_INR);
  728.         if(xedit[useron.xedit-1]->misc&WWIVCOLOR)
  729.             mode|=EX_WWIV; }
  730.     if(online==ON_LOCAL)
  731.         external(cmdstr(xedit[useron.xedit-1]->lcmd,str,nulstr,NULL),mode);
  732.     else {
  733.         rioctl(IOCM|PAUSE|ABORT);
  734.         external(cmdstr(xedit[useron.xedit-1]->rcmd,str,nulstr,NULL),mode);
  735.         rioctl(IOSM|PAUSE|ABORT); }
  736.     return; }
  737. if((buf=(char *)MALLOC(maxlines*MAX_LINE_LEN))==NULL) {
  738.     errormsg(WHERE,ERR_ALLOC,nulstr,maxlines*MAX_LINE_LEN);
  739.     return; }
  740. if((file=nopen(str,O_RDONLY))!=-1) {
  741.     length=filelength(file);
  742.     if(length>(ulong)maxlines*MAX_LINE_LEN) {
  743.         attr(color[clr_err]);
  744.         bprintf("\7\r\nFile size (%lu bytes) is larger than (%lu).\r\n"
  745.             ,length,(ulong)maxlines*MAX_LINE_LEN);
  746.         close(file);
  747.         FREE(buf); }
  748.     if(read(file,buf,length)!=length) {
  749.         close(file);
  750.         FREE(buf);
  751.         errormsg(WHERE,ERR_READ,str,length);
  752.         return; }
  753.     buf[length]=0;
  754.     close(file); }
  755. else {
  756.     buf[0]=0;
  757.     bputs(text[NewFile]); }
  758. if(!msgeditor(buf,nulstr,nulstr)) {
  759.     FREE(buf);
  760.     return; }
  761. bputs(text[Saving]);
  762. if((file=nopen(str,O_CREAT|O_WRONLY|O_TRUNC))==-1) {
  763.     errormsg(WHERE,ERR_OPEN,str,O_CREAT|O_WRONLY|O_TRUNC);
  764.     FREE(buf);
  765.     return; }
  766. if(write(file,buf,strlen(buf))!=strlen(buf)) {
  767.     close(file);
  768.     errormsg(WHERE,ERR_WRITE,str,strlen(buf));
  769.     FREE(buf);
  770.     return; }
  771. for(l=lines=0;buf[l];l++)
  772.     if(buf[l]==LF)
  773.         lines++;
  774. bprintf(text[SavedNBytes],l,lines);
  775. close(file);
  776. FREE(buf);
  777. return;
  778. }
  779.  
  780. /*************************/
  781. /* Copy file attachments */
  782. /*************************/
  783. void copyfattach(uint to, uint from, char *title)
  784. {
  785.     char str[128],str2[128],str3[128],*tp,*sp,*p;
  786.     uint i;
  787.  
  788. strcpy(str,title);
  789. tp=str;
  790. while(1) {
  791.     p=strchr(tp,SP);
  792.     if(p) *p=0;
  793.     sp=strrchr(tp,'/');              /* sp is slash pointer */
  794.     if(!sp) sp=strrchr(tp,'\\');
  795.     if(sp) tp=sp+1;
  796.     sprintf(str2,"%sFILE\\%04u.IN\\%s"  /* str2 is path/fname */
  797.         ,data_dir,to,tp);
  798.     sprintf(str3,"%sFILE\\%04u.IN\\%s"  /* str2 is path/fname */
  799.         ,data_dir,from,tp);
  800.     if(strcmp(str2,str3))
  801.         mv(str3,str2,1);
  802.     if(!p)
  803.         break;
  804.     tp=p+1; }
  805. }
  806.  
  807.  
  808. /****************************************************************************/
  809. /* Forwards mail (fname) to usernumber                                      */
  810. /* Called from function readmail                                            */
  811. /****************************************************************************/
  812. void forwardmail(smbmsg_t *msg, ushort usernumber)
  813. {
  814.     char str[256],touser[128];
  815.     int i;
  816.     node_t        node;
  817.     msghdr_t    hdr=msg->hdr;
  818.     idxrec_t    idx=msg->idx;
  819.  
  820. if(useron.etoday>=level_emailperday[useron.level] && !SYSOP) {
  821.     bputs(text[TooManyEmailsToday]);
  822.     return; }
  823. if(useron.rest&FLAG('F')) {
  824.     bputs(text[R_Forward]);
  825.     return; }
  826. if(usernumber==1 && useron.rest&FLAG('S')) {
  827.     bprintf(text[R_Feedback],sys_op);
  828.     return; }
  829. if(usernumber!=1 && useron.rest&FLAG('E')) {
  830.     bputs(text[R_Email]);
  831.     return; }
  832.  
  833. msg->idx.attr&=~(MSG_READ|MSG_DELETE);
  834. msg->hdr.attr=msg->idx.attr;
  835.  
  836.  
  837. smb_hfield(msg,SENDER,strlen(useron.alias),useron.alias);
  838. sprintf(str,"%u",useron.number);
  839. smb_hfield(msg,SENDEREXT,strlen(str),str);
  840.  
  841. username(usernumber,touser);
  842. smb_hfield(msg,RECIPIENT,strlen(touser),touser);
  843. sprintf(str,"%u",usernumber);
  844. smb_hfield(msg,RECIPIENTEXT,sizeof(str),str);
  845. msg->idx.to=usernumber;
  846.  
  847. now=time(NULL);
  848. smb_hfield(msg,FORWARDED,sizeof(time_t),&now);
  849.  
  850.  
  851. if((i=smb_open_da(&smb))!=0) {
  852.     errormsg(WHERE,ERR_OPEN,smb.file,i);
  853.     return; }
  854. if((i=smb_incdat(&smb,msg->hdr.offset,smb_getmsgdatlen(msg),1))!=0) {
  855.     errormsg(WHERE,ERR_WRITE,smb.file,i);
  856.     return; }
  857. smb_close_da(&smb);
  858.  
  859.  
  860. if((i=smb_addmsghdr(&smb,msg,SMB_SELFPACK))!=0) {
  861.     errormsg(WHERE,ERR_WRITE,smb.file,i);
  862.     return; }
  863.  
  864. if(msg->hdr.auxattr&MSG_FILEATTACH)
  865.     copyfattach(usernumber,useron.number,msg->subj);
  866.  
  867. bprintf(text[Forwarded],username(usernumber,str),usernumber);
  868. sprintf(str,"Forwarded mail to %s #%d",username(usernumber,tmp)
  869.     ,usernumber);
  870. logline("E",str);
  871. msg->idx=idx;
  872. msg->hdr=hdr;
  873.  
  874.  
  875. if(usernumber==1) {
  876.     useron.fbacks++;
  877.     logon_fbacks++;
  878.     putuserrec(useron.number,U_FBACKS,5,itoa(useron.fbacks,tmp,10)); }
  879. else {
  880.     useron.emails++;
  881.     logon_emails++;
  882.     putuserrec(useron.number,U_EMAILS,5,itoa(useron.emails,tmp,10)); }
  883. useron.etoday++;
  884. putuserrec(useron.number,U_ETODAY,5,itoa(useron.etoday,tmp,10));
  885.  
  886. for(i=1;i<=sys_nodes;i++) { /* Tell user, if online */
  887.     getnodedat(i,&node,0);
  888.     if(node.useron==usernumber && !(node.misc&NODE_POFF)
  889.         && (node.status==NODE_INUSE || node.status==NODE_QUIET)) {
  890.         sprintf(str,text[EmailNodeMsg],node_num,useron.alias);
  891.         putnmsg(i,str);
  892.         break; } }
  893. if(i>sys_nodes) {    /* User wasn't online, so leave short msg */
  894.     sprintf(str,text[UserSentYouMail],useron.alias);
  895.     putsmsg(usernumber,str); }
  896. }
  897.  
  898. /****************************************************************************/
  899. /* Auto-Message Routine ('A' from the main menu)                            */
  900. /****************************************************************************/
  901. void automsg()
  902. {
  903.     char str[256],buf[300],anon=0;
  904.     int file;
  905.     struct ffblk ff;
  906.  
  907. while(online) {
  908.     SYNC;
  909.     mnemonics(text[AutoMsg]);
  910.     switch(getkeys("RWQ",0)) {
  911.         case 'R':
  912.             sprintf(str,"%sMSGS\\AUTO.MSG",data_dir);
  913.             printfile(str,P_NOABORT|P_NOATCODES);
  914.             break;
  915.         case 'W':
  916.             if(useron.rest&FLAG('W')) {
  917.                 bputs(text[R_AutoMsg]);
  918.                 break; }
  919.             action=NODE_AMSG;
  920.             SYNC;
  921.             bputs("\r\n3 lines:\r\n");
  922.             if(!getstr(str,68,K_WRAP|K_MSG))
  923.                 break;
  924.             strcpy(buf,str);
  925.             strcat(buf,"\r\n          ");
  926.             getstr(str,68,K_WRAP|K_MSG);
  927.             strcat(buf,str);
  928.             strcat(buf,"\r\n          ");
  929.             getstr(str,68,K_MSG);
  930.             strcat(str,crlf);
  931.             strcat(buf,str);
  932.             if(yesno(text[OK])) {
  933.                 if(useron.exempt&FLAG('A')) {
  934.                     if(!noyes(text[AnonymousQ]))
  935.                         anon=1; }
  936.                 sprintf(str,"%sMSGS\\AUTO.MSG",data_dir);
  937.                 if((file=nopen(str,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
  938.                     errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT|O_TRUNC);
  939.                     return; }
  940.                 if(anon)
  941.                     sprintf(tmp,"%.80s",text[Anonymous]);
  942.                 else
  943.                     sprintf(tmp,"%s #%d",useron.alias,useron.number);
  944.                 sprintf(str,text[AutoMsgBy],tmp);
  945.                 strcat(str,"          ");
  946.                 write(file,str,strlen(str));
  947.                 write(file,buf,strlen(buf));
  948.                 close(file); }
  949.             break;
  950.         case 'Q':
  951.             return; } }
  952. }
  953.  
  954. /****************************************************************************/
  955. /* Edits messages                                                            */
  956. /****************************************************************************/
  957. void editmsg(smbmsg_t *msg, uint subnum)
  958. {
  959.     char    str[256],buf[SDT_BLOCK_LEN];
  960.     ushort    xlat;
  961.     int     file,i,j,x;
  962.     ulong    length,offset;
  963.     FILE    *instream;
  964.  
  965. if(!msg->hdr.total_dfields)
  966.     return;
  967. sprintf(str,"%sINPUT.MSG",node_dir);
  968. remove(str);
  969. msgtotxt(*msg,str,0,1);
  970. editfile(str);
  971. length=flength(str)+2;     /* +2 for translation string */
  972. if(length<1L)
  973.     return;
  974.  
  975. if((i=smb_locksmbhdr(&smb))!=0) {
  976.     errormsg(WHERE,ERR_LOCK,smb.file,i);
  977.     return; }
  978.  
  979. if((i=smb_getstatus(&smb))!=0) {
  980.     errormsg(WHERE,ERR_READ,smb.file,i);
  981.     return; }
  982.  
  983. if(!(smb.status.attr&SMB_HYPERALLOC)) {
  984.     if((i=smb_open_da(&smb))!=0) {
  985.         errormsg(WHERE,ERR_OPEN,smb.file,i);
  986.         return; }
  987.     for(x=0;x<msg->hdr.total_dfields;x++)
  988.         if((i=smb_freemsgdat(&smb,msg->hdr.offset+msg->dfield[x].offset
  989.             ,msg->dfield[x].length,1))!=0)
  990.             errormsg(WHERE,ERR_WRITE,smb.file,i); }
  991.  
  992. msg->dfield[0].type=TEXT_BODY;                /* Make one single data field */
  993. msg->dfield[0].length=length;
  994. msg->dfield[0].offset=0;
  995. for(x=1;x<msg->hdr.total_dfields;x++) {     /* Clear the other data fields */
  996.     msg->dfield[x].type=UNUSED;             /* so we leave the header length */
  997.     msg->dfield[x].length=0;                /* unchanged */
  998.     msg->dfield[x].offset=0; }
  999.  
  1000.  
  1001. if(smb.status.attr&SMB_HYPERALLOC) {
  1002.     offset=smb_hallocdat(&smb); }
  1003. else {
  1004.     if((subnum!=INVALID_SUB && sub[subnum]->misc&SUB_FAST)
  1005.         || (subnum==INVALID_SUB && sys_misc&SM_FASTMAIL))
  1006.         offset=smb_fallocdat(&smb,length,1);
  1007.     else
  1008.         offset=smb_allocdat(&smb,length,1);
  1009.     smb_close_da(&smb); }
  1010.  
  1011. msg->hdr.offset=offset;
  1012. if((file=open(str,O_RDONLY|O_BINARY))==-1
  1013.     || (instream=fdopen(file,"rb"))==NULL) {
  1014.     smb_unlocksmbhdr(&smb);
  1015.     smb_freemsgdat(&smb,offset,length,1);
  1016.     errormsg(WHERE,ERR_OPEN,str,O_RDONLY|O_BINARY);
  1017.     return; }
  1018.  
  1019. setvbuf(instream,NULL,_IOFBF,2*1024);
  1020. fseek(smb.sdt_fp,offset,SEEK_SET);
  1021. xlat=XLAT_NONE;
  1022. fwrite(&xlat,2,1,smb.sdt_fp);
  1023. x=SDT_BLOCK_LEN-2;                /* Don't read/write more than 255 */
  1024. while(!feof(instream)) {
  1025.     memset(buf,0,x);
  1026.     j=fread(buf,1,x,instream);
  1027.     if((j!=x || feof(instream)) && buf[j-1]==LF && buf[j-2]==CR)
  1028.         buf[j-1]=buf[j-2]=0;    /* Convert to NULL */
  1029.     fwrite(buf,j,1,smb.sdt_fp);
  1030.     x=SDT_BLOCK_LEN; }
  1031. fflush(smb.sdt_fp);
  1032. fclose(instream);
  1033.  
  1034. smb_unlocksmbhdr(&smb);
  1035. msg->hdr.length=smb_getmsghdrlen(msg);
  1036. if((i=smb_putmsghdr(&smb,msg))!=0)
  1037.     errormsg(WHERE,ERR_WRITE,smb.file,i);
  1038. }
  1039.  
  1040. /****************************************************************************/
  1041. /* Moves a message from one message base to another                         */
  1042. /****************************************************************************/
  1043. char movemsg(smbmsg_t msg, uint subnum)
  1044. {
  1045.     char str[256],*buf;
  1046.     int i,j,x,file,newgrp,newsub,storage;
  1047.     long l;
  1048.     ulong offset,length;
  1049.  
  1050. for(i=0;i<usrgrps;i++)         /* Select New Group */
  1051.     uselect(1,i,"Message Group",grp[usrgrp[i]]->lname,0);
  1052. if((newgrp=uselect(0,0,0,0,0))<0)
  1053.     return(0);
  1054.  
  1055. for(i=0;i<usrsubs[newgrp];i++)         /* Select New Sub-Board */
  1056.     uselect(1,i,"Sub-Board",sub[usrsub[newgrp][i]]->lname,0);
  1057. if((newsub=uselect(0,0,0,0,0))<0)
  1058.     return(0);
  1059. newsub=usrsub[newgrp][newsub];
  1060.  
  1061. length=smb_getmsgdatlen(&msg);
  1062. if((buf=(char *)MALLOC(length))==NULL) {
  1063.     errormsg(WHERE,ERR_ALLOC,smb.file,length);
  1064.     return(0); }
  1065.  
  1066. fseek(smb.sdt_fp,msg.hdr.offset,SEEK_SET);
  1067. fread(buf,length,1,smb.sdt_fp);
  1068.  
  1069. if((i=smb_stack(&smb,SMB_STACK_PUSH))!=0) {
  1070.     FREE(buf);
  1071.     errormsg(WHERE,ERR_OPEN,sub[newsub]->code,i);
  1072.     return(0); }
  1073.  
  1074. sprintf(smb.file,"%s%s",sub[newsub]->data_dir,sub[newsub]->code);
  1075. smb.retry_time=smb_retry_time;
  1076. if((i=smb_open(&smb))!=0) {
  1077.     FREE(buf);
  1078.     smb_stack(&smb,SMB_STACK_POP);
  1079.     errormsg(WHERE,ERR_OPEN,smb.file,i);
  1080.     return(0); }
  1081.  
  1082. if(filelength(fileno(smb.shd_fp))<1) {     /* Create it if it doesn't exist */
  1083.     smb.status.max_crcs=sub[newsub]->maxcrcs;
  1084.     smb.status.max_msgs=sub[newsub]->maxmsgs;
  1085.     smb.status.max_age=sub[newsub]->maxage;
  1086.     smb.status.attr=sub[newsub]->misc&SUB_HYPER ? SMB_HYPERALLOC :0;
  1087.     if((i=smb_create(&smb))!=0) {
  1088.         FREE(buf);
  1089.         smb_close(&smb);
  1090.         smb_stack(&smb,SMB_STACK_POP);
  1091.         errormsg(WHERE,ERR_CREATE,smb.file,i);
  1092.         return(0); } }
  1093.  
  1094. if((i=smb_locksmbhdr(&smb))!=0) {
  1095.     FREE(buf);
  1096.     smb_close(&smb);
  1097.     smb_stack(&smb,SMB_STACK_POP);
  1098.     errormsg(WHERE,ERR_LOCK,smb.file,i);
  1099.     return(0); }
  1100.  
  1101. if((i=smb_getstatus(&smb))!=0) {
  1102.     FREE(buf);
  1103.     smb_close(&smb);
  1104.     smb_stack(&smb,SMB_STACK_POP);
  1105.     errormsg(WHERE,ERR_READ,smb.file,i);
  1106.     return(0); }
  1107.  
  1108. if(smb.status.attr&SMB_HYPERALLOC) {
  1109.     offset=smb_hallocdat(&smb);
  1110.     storage=SMB_HYPERALLOC; }
  1111. else {
  1112.     if((i=smb_open_da(&smb))!=0) {
  1113.         FREE(buf);
  1114.         smb_close(&smb);
  1115.         smb_stack(&smb,SMB_STACK_POP);
  1116.         errormsg(WHERE,ERR_OPEN,smb.file,i);
  1117.         return(0); }
  1118.     if(sub[newsub]->misc&SUB_FAST) {
  1119.         offset=smb_fallocdat(&smb,length,1);
  1120.         storage=SMB_FASTALLOC; }
  1121.     else {
  1122.         offset=smb_allocdat(&smb,length,1);
  1123.         storage=SMB_SELFPACK; }
  1124.     smb_close_da(&smb); }
  1125.  
  1126. msg.hdr.offset=offset;
  1127.  
  1128. memcpy(msg.hdr.id,"SHD\x1a",4);
  1129. msg.hdr.version=SMB_VERSION;
  1130.  
  1131. smb_unlocksmbhdr(&smb);
  1132.  
  1133. fseek(smb.sdt_fp,offset,SEEK_SET);
  1134. fwrite(buf,length,1,smb.sdt_fp);
  1135. fflush(smb.sdt_fp);
  1136. FREE(buf);
  1137.  
  1138. i=smb_addmsghdr(&smb,&msg,storage);
  1139. smb_close(&smb);
  1140. smb_stack(&smb,SMB_STACK_POP);
  1141.  
  1142. if(i) {
  1143.     smb_freemsgdat(&smb,offset,length,1);
  1144.     errormsg(WHERE,ERR_WRITE,smb.file,i);
  1145.     return(0); }
  1146.  
  1147. bprintf("\r\nMoved to %s %s\r\n\r\n"
  1148.     ,grp[usrgrp[newgrp]]->sname,sub[newsub]->lname);
  1149. sprintf(str,"Moved message from %s %s to %s %s"
  1150.     ,grp[newgrp]->sname,sub[newsub]->sname
  1151.     ,grp[sub[subnum]->grp]->sname,sub[subnum]->sname);
  1152. logline("M+",str);
  1153. if(sub[newsub]->misc&SUB_FIDO && sub[newsub]->echomail_sem[0])
  1154.     if((file=nopen(sub[newsub]->echomail_sem
  1155.         ,O_WRONLY|O_CREAT|O_TRUNC))!=-1)
  1156.         close(file);
  1157. return(1);
  1158. }
  1159.  
  1160. ushort chmsgattr(ushort attr)
  1161. {
  1162.     int ch;
  1163.  
  1164. while(online && !(sys_status&SS_ABORT)) {
  1165.     CRLF;
  1166.     show_msgattr(attr);
  1167.     menu("MSGATTR");
  1168.     ch=getkey(K_UPPER);
  1169.     if(ch)
  1170.         bprintf("%c\r\n",ch);
  1171.     switch(ch) {
  1172.         case 'P':
  1173.             attr^=MSG_PRIVATE;
  1174.             break;
  1175.         case 'R':
  1176.             attr^=MSG_READ;
  1177.             break;
  1178.         case 'K':
  1179.             attr^=MSG_KILLREAD;
  1180.             break;
  1181.         case 'A':
  1182.             attr^=MSG_ANONYMOUS;
  1183.             break;
  1184.         case 'N':   /* Non-purgeable */
  1185.             attr^=MSG_PERMANENT;
  1186.             break;
  1187.         case 'M':
  1188.             attr^=MSG_MODERATED;
  1189.             break;
  1190.         case 'V':
  1191.             attr^=MSG_VALIDATED;
  1192.             break;
  1193.         case 'D':
  1194.             attr^=MSG_DELETE;
  1195.             break;
  1196.         case 'L':
  1197.             attr^=MSG_LOCKED;
  1198.             break;
  1199.         default:
  1200.             return(attr); } }
  1201. return(attr);
  1202. }
  1203.