writemsg.cpp 43.4 KB
Newer Older
1
/* Synchronet message creation routines */
2
// vi: tabstop=4
3
4
5
6
7
8
9

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
38
#include "wordwrap.h"
39

40
#define MAX_LINES		10000
rswindell's avatar
rswindell committed
41
#define MAX_LINE_LEN	(cols - 1)
42

43
const char *quote_fmt=" > %.*s\r\n";
44
45
void quotestr(char *str);

46
47
48
/****************************************************************************/
/* Returns temporary message text filename (for message/text editors)		*/
/****************************************************************************/
49
char* sbbs_t::msg_tmp_fname(int xedit, char* path, size_t len)
50
{
51
	safe_snprintf(path, len, "%sINPUT.MSG", cfg.temp_dir);
52

53
	if(xedit && chk_ar(cfg.xedit[xedit-1]->ar, &useron, &client)) {
54
		if(cfg.xedit[xedit-1]->misc&QUICKBBS)
55
			safe_snprintf(path, len, "%sMSGTMP", cfg.node_dir);	/* QuickBBS editors are dumb */
56
		if(cfg.xedit[xedit-1]->misc&XTRN_LWRCASE)
57
			strlwr(getfname(path));
58
59
	}

60
61
62
63
64
65
66
67
	return path;
}

/****************************************************************************/
/****************************************************************************/
char* sbbs_t::quotes_fname(int xedit, char *path, size_t len)
{
	safe_snprintf(path, len, "%sQUOTES.TXT", cfg.node_dir);
68
69
70
	if(xedit 
		&& chk_ar(cfg.xedit[xedit-1]->ar, &useron, &client) 
		&& (cfg.xedit[xedit-1]->misc&XTRN_LWRCASE))
71
72
73
74
75
76
		strlwr(getfname(path));
	return path;
}

/****************************************************************************/
/****************************************************************************/
77
bool sbbs_t::quotemsg(smb_t* smb, smbmsg_t* msg, bool tails)
78
{
79
80
81
82
	char	fname[MAX_PATH+1];
	char*	buf;
	char*	wrapped=NULL;
	FILE*	fp;
83
	ushort useron_xedit = useron.xedit;
84
85
	uint8_t	org_cols = TERM_COLS_DEFAULT;

86
87
	if(msg->columns != 0)
		org_cols = msg->columns;
88

89
90
91
92
	if(useron_xedit && !chk_ar(cfg.xedit[useron_xedit-1]->ar, &useron, &client))
		useron_xedit = 0;

	quotes_fname(useron_xedit,fname,sizeof(fname));
93
	removecase(fname);
94

95
96
	if((fp=fopen(fname,"w"))==NULL) {
		errormsg(WHERE,ERR_OPEN,fname,0);
97
		return false; 
98
99
	}

100
	bool result = false;
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
	ulong mode = GETMSGTXT_PLAIN;
	if(tails) mode |= GETMSGTXT_TAILS;
	if((buf=smb_getmsgtxt(smb, msg, mode)) != NULL) {
		strip_invalid_attr(buf);
		truncsp(buf);
		if(!useron_xedit || (useron_xedit && (cfg.xedit[useron_xedit-1]->misc&QUOTEWRAP)))
			wrapped=::wordwrap(buf, cols-4, org_cols - 1, /* handle_quotes: */TRUE);
		if(wrapped!=NULL) {
			fputs(wrapped,fp);
			free(wrapped);
		} else
			fputs(buf,fp);
		smb_freemsgtxt(buf); 
		result = true;
	} else if(smb_getmsgdatlen(msg)>2)
		errormsg(WHERE,ERR_READ,smb->file,smb_getmsgdatlen(msg));
117

118
	fclose(fp);
119
	return result;
120
121
122
123
}

/****************************************************************************/
/****************************************************************************/
124
int sbbs_t::process_edited_text(char* buf, FILE* stream, long mode, unsigned* lines, unsigned maxlines)
125
{
126
	unsigned i,l;
127
	int	len=0;
128
129
130
131
	ushort useron_xedit = useron.xedit;

	if(useron_xedit && !chk_ar(cfg.xedit[useron_xedit-1]->ar, &useron, &client))
		useron_xedit = 0;
132

133
	for(l=i=0;buf[l] && i<maxlines;l++) {
134
135
		if((uchar)buf[l]==141 && useron_xedit
    		&& cfg.xedit[useron_xedit-1]->misc&QUICKBBS) {
136
137
138
139
140
			len+=fwrite(crlf,1,2,stream);
			i++;
			continue; 
		}
		/* Expand LF to CRLF? */
141
142
		if(buf[l]==LF && (!l || buf[l-1]!=CR) && useron_xedit
			&& cfg.xedit[useron_xedit-1]->misc&EXPANDLF) {
143
144
145
146
147
			len+=fwrite(crlf,1,2,stream);
			i++;
			continue; 
		}
		/* Strip FidoNet Kludge Lines? */
148
149
		if(buf[l]==CTRL_A && useron_xedit
			&& cfg.xedit[useron_xedit-1]->misc&STRIPKLUDGE) {
150
151
152
153
154
155
			while(buf[l] && buf[l]!=LF) 
				l++;
			if(buf[l]==0)
				break;
			continue;
		}
156
		if(!(mode&(WM_EMAIL|WM_NETMAIL|WM_EDIT))
157
158
159
160
161
162
163
164
165
166
167
			&& (!l || buf[l-1]==LF)
			&& buf[l]=='-' && buf[l+1]=='-' && buf[l+2]=='-'
			&& (buf[l+3]==' ' || buf[l+3]==TAB || buf[l+3]==CR))
			buf[l+1]='+';
		if(buf[l]==LF)
			i++;
		fputc(buf[l],stream); 
		len++;
	}

	if(buf[l])
168
		bprintf(text[NoMoreLines], i);
169
170
171
172
173
174
175
176

	if(lines!=NULL)
		*lines=i;
	return len;
}

/****************************************************************************/
/****************************************************************************/
177
int sbbs_t::process_edited_file(const char* src, const char* dest, long mode, unsigned* lines, unsigned maxlines)
178
179
180
181
182
{
	char*	buf;
	long	len;
	FILE*	fp;

183
	if((len=(long)flength(src))<1)
184
185
186
187
188
		return -1;

	if((buf=(char*)malloc(len+1))==NULL)
		return -2;

rswindell's avatar
rswindell committed
189
190
	if((fp=fopen(src,"rb"))==NULL) {
		free(buf);
191
		return -3;
rswindell's avatar
rswindell committed
192
	}
193
194
195
196
197
198

	memset(buf,0,len+1);
	fread(buf,len,sizeof(char),fp);
	fclose(fp);

	if((fp=fopen(dest,"wb"))!=NULL) {
199
		len=process_edited_text(buf, fp, mode, lines, maxlines);
200
201
202
203
204
		fclose(fp);
	}
	free(buf);

	return len;
205
206
}

207
208
209
210
211
212
/****************************************************************************/
/* Creates a message (post or mail) using standard line editor. 'fname' is  */
/* is name of file to create, 'top' is a buffer to place at beginning of    */
/* message and 'title' is the title (70chars max) for the message.          */
/* 'dest' contains a text description of where the message is going.        */
/****************************************************************************/
213
214
bool sbbs_t::writemsg(const char *fname, const char *top, char *subj, long mode, uint subnum
	,const char *to, const char* from, char** editor)
215
{
216
	char	str[256],quote[128],c,*buf,*p,*tp
217
				,useron_level;
218
	char	msgtmp[MAX_PATH+1];
219
	char	tagfile[MAX_PATH+1];
220
221
	char	draft_desc[128];
	char	draft[MAX_PATH + 1];
222
223
	char 	tmp[512];
	int		i,j,file,linesquoted=0;
224
	long	length,qlen=0,qtime=0,ex_mode=0;
225
	ulong	l;
226
227
	FILE*	stream;
	FILE*	fp;
228
	unsigned lines;
229
230
	ushort useron_xedit = useron.xedit;

rswindell's avatar
rswindell committed
231
232
233
234
235
	if(cols < 2) {
		errormsg(WHERE, ERR_CHK, "columns", cols);
		return false;
	}

236
237
238
	if(top == NULL)
		top = "";

239
240
	if(useron_xedit && !chk_ar(cfg.xedit[useron_xedit-1]->ar, &useron, &client))
		useron_xedit = 0;
241
242
243

	useron_level=useron.level;

244
245
246
	if(editor!=NULL)
		*editor=NULL;

rswindell's avatar
rswindell committed
247
	if((buf=(char*)malloc((cfg.level_linespermsg[useron_level]*MAX_LINE_LEN) + 1))
248
249
		==NULL) {
		errormsg(WHERE,ERR_ALLOC,fname
rswindell's avatar
rswindell committed
250
			,(cfg.level_linespermsg[useron_level]*MAX_LINE_LEN) +1);
251
252
		return(false); 
	}
253
254
255
256
257

	if(mode&WM_NETMAIL ||
		(!(mode&(WM_EMAIL|WM_NETMAIL)) && cfg.sub[subnum]->misc&SUB_PNET))
		mode|=WM_NOTOP;

258
	msg_tmp_fname(useron_xedit, msgtmp, sizeof(msgtmp));
259
	removecase(msgtmp);
260
261
	SAFEPRINTF(tagfile,"%seditor.tag",cfg.temp_dir);
	removecase(tagfile);
262
263
	SAFEPRINTF(draft_desc, "draft.%s.msg", subnum >= cfg.total_subs ? "mail" : cfg.sub[subnum]->code);
	SAFEPRINTF3(draft, "%suser/%04u.%s", cfg.data_dir, useron.number, draft_desc);
264

265
	bool draft_restored = false;
266
267
268
269
	if(flength(draft) > 0 && (time(NULL) - fdate(draft)) < 48L*60L*60L && yesno("Unsaved draft message found. Use it")) {
		if(mv(draft, msgtmp, /* copy: */true) == 0) {
			lprintf(LOG_NOTICE, "draft message restored: %s (%lu bytes)", draft, (ulong)flength(msgtmp));
			draft_restored = true;
270
			removecase(quotes_fname(useron_xedit, str, sizeof(str)));
271
		} else
rswindell's avatar
rswindell committed
272
			lprintf(LOG_ERR, "ERROR %d (%s) restoring draft message: %s", errno, strerror(errno), draft);
273
	}
274
	else if(mode&WM_QUOTE && !(useron.rest&FLAG('J'))
275
276
277
278
279
280
		&& ((mode&(WM_EMAIL|WM_NETMAIL) && cfg.sys_misc&SM_QUOTE_EM)
		|| (!(mode&(WM_EMAIL|WM_NETMAIL)) && (uint)subnum!=INVALID_SUB
			&& cfg.sub[subnum]->misc&SUB_QUOTE))) {

		/* Quote entire message to MSGTMP or INPUT.MSG */

281
282
		if(useron_xedit && cfg.xedit[useron_xedit-1]->misc&QUOTEALL) {
			quotes_fname(useron_xedit, str, sizeof(str));
283
			if((stream=fnopen(NULL,str,O_RDONLY))==NULL) {
284
				errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
285
				free(buf);
286
287
				return(false); 
			}
288
289
			if((file=nopen(msgtmp,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
				errormsg(WHERE,ERR_OPEN,msgtmp,O_WRONLY|O_CREAT|O_TRUNC);
290
				free(buf);
291
				fclose(stream);
292
293
				return(false); 
			}
294
295

			while(!feof(stream) && !ferror(stream)) {
296
				if(!fgets(str,sizeof(str),stream))
297
298
					break;
				quotestr(str);
299
				SAFEPRINTF2(tmp,quote_fmt,cols-4,str);
300
				write(file,tmp,strlen(tmp));
301
302
				linesquoted++; 
			}
303
			fclose(stream);
304
305
			close(file); 
		}
306
307
308

		/* Quote nothing to MSGTMP or INPUT.MSG automatically */

309
		else if(useron_xedit && cfg.xedit[useron_xedit-1]->misc&QUOTENONE)
310
311
312
			;

		else if(yesno(text[QuoteMessageQ])) {
313
			quotes_fname(useron_xedit, str, sizeof(str));
314
315
			if((stream=fnopen(&file,str,O_RDONLY))==NULL) {
				errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
316
				free(buf);
317
318
				return(false); 
			}
319

320
321
			if((file=nopen(msgtmp,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
				errormsg(WHERE,ERR_OPEN,msgtmp,O_WRONLY|O_CREAT|O_TRUNC);
322
				free(buf);
323
				fclose(stream);
324
325
				return(false); 
			}
326

327
			l=(long)ftell(stream);			/* l now points to start of message */
328
329

			while(online) {
330
				SAFEPRINTF(str,text[QuoteLinesPrompt],linesquoted ? "Done":"All");
331
332
333
334
335
				mnemonics(str);
				i=getstr(quote,10,K_UPPER);
				if(sys_status&SS_ABORT) {
					fclose(stream);
					close(file);
336
					free(buf);
337
338
					return(false); 
				}
339
340
341
342
343
				if(!i && linesquoted)
					break;
				if(!i || quote[0]=='A') {                   /* Quote all */
					fseek(stream,l,SEEK_SET);
					while(!feof(stream) && !ferror(stream)) {
344
						if(!fgets(str,sizeof(str),stream))
345
346
							break;
						quotestr(str);
347
						SAFEPRINTF2(tmp,quote_fmt,cols-4,str);
348
						write(file,tmp,strlen(tmp));
349
350
351
352
						linesquoted++; 
					}
					break; 
				}
353
354
355
356
357
358
				if(quote[0]=='L') {
					fseek(stream,l,SEEK_SET);
					i=1;
					CRLF;
					attr(LIGHTGRAY);
					while(!feof(stream) && !ferror(stream) && !msgabort()) {
359
						if(!fgets(str,sizeof(str),stream))
360
361
362
							break;
						quotestr(str);
						bprintf("%3d: %.74s\r\n",i,str);
363
364
365
366
						i++; 
					}
					continue; 
				}
367
368
369
370
371

				if(!isdigit(quote[0]))
					break;
				p=quote;
				while(p) {
372
					if(*p==',' || *p==' ')
373
374
375
376
377
378
379
						p++;
					i=atoi(p);
					if(!i)
						break;
					fseek(stream,l,SEEK_SET);
					j=1;
					while(!feof(stream) && !ferror(stream) && j<i) {
380
						if(!fgets(tmp,sizeof(tmp),stream))
381
							break;
382
383
						j++; /* skip beginning */
					}		
384
385
386
387
					tp=strchr(p,'-');   /* tp for temp pointer */
					if(tp) {		 /* range */
						i=atoi(tp+1);
						while(!feof(stream) && !ferror(stream) && j<=i) {
388
							if(!fgets(str,sizeof(str),stream))
389
390
								break;
							quotestr(str);
391
							SAFEPRINTF2(tmp,quote_fmt,cols-4,str);
392
393
							write(file,tmp,strlen(tmp));
							linesquoted++;
394
395
396
							j++; 
						} 
					}
397
					else {			/* one line */
398
						if(fgets(str,sizeof(str),stream)) {
399
							quotestr(str);
400
							SAFEPRINTF2(tmp,quote_fmt,cols-4,str);
401
							write(file,tmp,strlen(tmp));
402
403
404
							linesquoted++; 
						} 
					}
405
					p=strchr(p,',');
406
					// if(!p) p=strchr(p,' ');  02/05/96 huh?
407
408
				} 
			}
409
410

			fclose(stream);
411
412
413
			close(file); 
		} 
	}
414
415
416
	else {
		removecase(quotes_fname(useron_xedit, str, sizeof(str)));
	}
417
418

	if(!online || sys_status&SS_ABORT) {
419
		free(buf);
420
421
		return(false); 
	}
422

423
	if(!(mode&(WM_EXTDESC|WM_SUBJ_RO))) {
rswindell's avatar
Bugfix:    
rswindell committed
424
425
		int	max_title_len;

426
427
		if(mode&WM_FILE) {
			CRLF;
428
429
			bputs(text[Filename]); 
		}
430
		else {
431
432
			bputs(text[SubjectPrompt]); 
		}
rswindell's avatar
Bugfix:    
rswindell committed
433
434
435
		max_title_len=cols-column-1;
		if(max_title_len > LEN_TITLE)
			max_title_len = LEN_TITLE;
436
437
		if(draft_restored)
			user_get_property(&cfg, useron.number, draft_desc, "subject", subj, max_title_len);
438
		if(!getstr(subj,max_title_len,mode&WM_FILE ? K_LINE|K_TRIM : K_LINE|K_EDIT|K_AUTODEL|K_TRIM)
439
			&& useron_level && useron.logons) {
440
			free(buf);
441
442
			return(false); 
		}
443
		if((mode&WM_FILE) && !checkfname(subj)) {
444
445
446
447
			free(buf);
			bputs(text[BadFilename]);
			return(false);
		}
448
449
		if(!(mode&(WM_EMAIL|WM_NETMAIL)) && cfg.sub[subnum]->misc&SUB_QNET
			&& !SYSOP
450
451
			&& (!stricmp(subj,"DROP") || !stricmp(subj,"ADD")
			|| !strnicmp(to,"SBBS",4))) {
452
			free(buf);   /* Users can't post DROP or ADD in QWK netted subs */
453
454
455
			return(false); /* or messages to "SBBS" */
		}
	}
456
457

	if(!online || sys_status&SS_ABORT) {
458
		free(buf);
459
460
		return(false); 
	}
461

462
463
	smb.subnum = subnum;	/* Allow JS msgeditors to use bbs.smb_sub* */

464
465
466
467
468
469
	if(console&CON_RAW_IN) {
		bprintf(text[EnterMsgNowRaw]
			,(ulong)cfg.level_linespermsg[useron_level]*MAX_LINE_LEN);
		if(top[0] && !(mode&WM_NOTOP)) {
			strcpy((char *)buf,top);
			strcat((char *)buf,crlf);
470
471
			l=strlen((char *)buf); 
		}
472
473
474
475
476
		else
			l=0;
		while(l<(ulong)(cfg.level_linespermsg[useron_level]*MAX_LINE_LEN)) {
			c=getkey(0);
			if(sys_status&SS_ABORT) {  /* Ctrl-C */
477
				free(buf);
478
479
				return(false); 
			}
480
			if((c==ESC || c==CTRL_A) && useron.rest&FLAG('A')) /* ANSI restriction */
481
				continue;
482
			if(c==BEL && useron.rest&FLAG('B'))   /* Beep restriction */
483
484
485
486
				continue;
			if(!(console&CON_RAW_IN))	/* Ctrl-Z was hit */
				break;
			outchar(c);
487
488
			buf[l++]=c; 
		}
489
490
		buf[l]=0;
		if(l==(ulong)cfg.level_linespermsg[useron_level]*MAX_LINE_LEN)
491
492
			bputs(text[OutOfBytes]); 
	}
493
494


495
	else if(useron_xedit) {
496

497
		if(editor!=NULL)
498
			*editor=cfg.xedit[useron_xedit-1]->name;
499

500
		editor_inf(useron_xedit,to,from,subj,mode,subnum,tagfile);
501
		if(cfg.xedit[useron_xedit-1]->type) {
502
			gettimeleft();
503
			xtrndat(mode&WM_ANON ? text[Anonymous]:from,cfg.node_dir,cfg.xedit[useron_xedit-1]->type
504
 			   ,timeleft,cfg.xedit[useron_xedit-1]->misc); 
505
506
		}

507
		if(cfg.xedit[useron_xedit-1]->misc&XTRN_STDIO) {
508
			ex_mode|=EX_STDIO;
509
			if(cfg.xedit[useron_xedit-1]->misc&WWIVCOLOR)
510
511
				ex_mode|=EX_WWIV; 
		}
512
		if(cfg.xedit[useron_xedit-1]->misc&XTRN_NATIVE)
513
			ex_mode|=EX_NATIVE;
514
		if(cfg.xedit[useron_xedit-1]->misc&XTRN_SH)
515
			ex_mode|=EX_SH;
516

517
518
519
520
521
522
523
		if(!draft_restored) {
			if(!linesquoted)
				removecase(msgtmp);
			else {
				qlen=(long)flength(msgtmp);
				qtime=(long)fdate(msgtmp); 
			}
524
		}
525

526
527
		CLS;
		rioctl(IOCM|PAUSE|ABORT);
528
529
530
		const char* cmd = cmdstr(cfg.xedit[useron_xedit-1]->rcmd, msgtmp, nulstr, NULL);
		int result = external(cmd, ex_mode, cfg.node_dir);
		lprintf(LOG_DEBUG, "'%s' returned %d", cmd, result);
531
532
		rioctl(IOSM|PAUSE|ABORT); 

533
		checkline();
534
		if(!online && flength(msgtmp) > 0)	 { // save draft message due to disconnection
535
536
537
			if(mv(msgtmp, draft, /* copy: */true) == 0) {
				user_set_property(&cfg, useron.number, draft_desc, "subject", subj);
				user_set_time_property(&cfg, useron.number, draft_desc, "created", time(NULL));
538
539
				lprintf(LOG_NOTICE, "draft message saved: %s (%lu bytes)", draft, (ulong)flength(draft));
			} else
rswindell's avatar
rswindell committed
540
				lprintf(LOG_ERR, "ERROR %d (%s) saving draft message: %s", errno, strerror(errno), draft);
541
542
543
		}

		if(result != EXIT_SUCCESS || !fexistcase(msgtmp) || !online
544
			|| (linesquoted && qlen==flength(msgtmp) && qtime==fdate(msgtmp))) {
545
			free(buf);
546
547
			return(false); 
		}
548
		SAFEPRINTF(str,"%sRESULT.ED",cfg.node_dir);
549
		if(!(mode&(WM_EXTDESC|WM_FILE|WM_SUBJ_RO))
550
			&& !(cfg.xedit[useron_xedit-1]->misc&QUICKBBS) 
551
552
553
554
555
			&& fexistcase(str)) {
			if((fp=fopen(str,"r")) != NULL) {
				fgets(str,sizeof(str),fp);
				fgets(str,sizeof(str),fp);
				truncsp(str);
556
				safe_snprintf(subj,LEN_TITLE,"%s",str);
557
558
559
560
				fclose(fp);
			}
		}

561
562
563
		buf[0]=0;
		if(!(mode&WM_NOTOP))
			strcpy((char *)buf,top);
564
565
		if((file=nopen(msgtmp,O_RDONLY))==-1) {
			errormsg(WHERE,ERR_OPEN,msgtmp,O_RDONLY);
566
			free(buf);
567
568
			return(false); 
		}
569
		length=(long)filelength(file);
570
571
572
573
		l=strlen((char *)buf);	  /* reserve space for top and terminating null */
		/* truncate if too big */
		if(length>(long)((cfg.level_linespermsg[useron_level]*MAX_LINE_LEN)-(l+1))) {
			length=(cfg.level_linespermsg[useron_level]*MAX_LINE_LEN)-(l+1);
574
575
			bputs(text[OutOfBytes]); 
		}
576
577
		lread(file,buf+l,length);
		close(file);
578
		// remove(msgtmp); 	   /* no need to save the temp input file */
579
580
		buf[l+length]=0; 
	}
581
582
	else {
		buf[0]=0;
583
		if(linesquoted || draft_restored) {
584
			if((file=nopen(msgtmp,O_RDONLY))!=-1) {
585
				length=(long)filelength(file);
586
587
				l=length>(cfg.level_linespermsg[useron_level]*MAX_LINE_LEN)-1
					? (cfg.level_linespermsg[useron_level]*MAX_LINE_LEN)-1 : length;
588
589
590
				lread(file,buf,l);
				buf[l]=0;
				close(file);
591
				// remove(msgtmp);
592
593
			} 
		}
594
		if(!(msgeditor((char *)buf,mode&WM_NOTOP ? nulstr : top, subj))) {
595
596
597
598
599
600
601
602
603
604
			if(!online) {
				FILE* fp = fopen(draft, "wb");
				if(fp == NULL)
					errormsg(WHERE, ERR_CREATE, draft, O_WRONLY);
				else {
					fputs(buf, fp);
					fclose(fp);
					user_set_property(&cfg, useron.number, draft_desc, "subject", subj);
				}
			}
605
			free(buf);	/* Assertion here Dec-17-2003, think I fixed in block above (rev 1.52) */
606
607
608
			return(false); 
		} 
	}
609
610
611

	now=time(NULL);
	bputs(text[Saving]);
612
	removecase(fname);
613
	if((stream=fnopen(NULL,fname,O_WRONLY|O_CREAT|O_TRUNC))==NULL) {
614
		errormsg(WHERE,ERR_OPEN,fname,O_WRONLY|O_CREAT|O_TRUNC);
615
		free(buf);
616
617
		return(false); 
	}
618
	l=process_edited_text(buf,stream,mode,&lines,cfg.level_linespermsg[useron_level]);
619

620
	if(!(mode&(WM_EXTDESC|WM_ANON))) {
621
622
623
624
625
		/* Signature file */
		if((subnum==INVALID_SUB && cfg.msg_misc&MM_EMAILSIG)
			|| (subnum!=INVALID_SUB && !(cfg.sub[subnum]->misc&SUB_NOUSERSIG))) {
			SAFEPRINTF2(str,"%suser/%04u.sig",cfg.data_dir,useron.number);
			FILE* sig;
626
			if(fexistcase(str) && (sig=fopen(str,"r"))!=NULL) {
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
				while(!feof(sig)) {
					if(!fgets(str,sizeof(str),sig))
						break;
					truncsp(str);
					l+=fprintf(stream,"%s\r\n",str);
					lines++;		/* line counter */
				}
				fclose(sig);
			}
		}
		if(fexistcase(tagfile)) {
			FILE* tag;

			if((tag=fopen(tagfile,"r")) != NULL) {
				while(!feof(tag)) {
					if(!fgets(str,sizeof(str),tag))
						break;
					truncsp(str);
					l+=fprintf(stream,"%s\r\n",str);
					lines++;		/* line counter */
				}
				fclose(tag);
649
			}
650
651
652
		}
	}

653
	remove(draft);
654
	fclose(stream);
655
	free((char *)buf);
656
	bprintf(text[SavedNBytes],l,lines);
657
658
659
	return(true);
}

660
661
662
663
664
665
666
667
668
669
670
671
672
673
void sbbs_t::editor_info_to_msg(smbmsg_t* msg, const char* editor)
{
	if(editor != NULL)
		smb_hfield_str(msg, SMB_EDITOR, editor);

	ushort useron_xedit = useron.xedit;

	if(useron_xedit > 0 && !chk_ar(cfg.xedit[useron_xedit - 1]->ar, &useron, &client))
		useron_xedit = 0;

	if(editor == NULL || useron_xedit == 0 || (cfg.xedit[useron_xedit - 1]->misc&SAVECOLUMNS))
		smb_hfield_bin(msg, SMB_COLUMNS, cols);
}

674
/****************************************************************************/
675
676
677
678
679
/****************************************************************************/
/* Modify 'str' to for quoted format. Remove ^A codes, etc.                 */
/****************************************************************************/
void quotestr(char *str)
{
rswindell's avatar
rswindell committed
680
681
	truncsp(str);
	remove_ctrl_a(str,str);
682
683
}

684
685
/****************************************************************************/
/****************************************************************************/
686
void sbbs_t::editor_inf(int xeditnum, const char *to, const char* from, const char *subj, long mode
687
	,uint subnum, const char* tagfile)
688
{
689
690
	char	path[MAX_PATH+1];
	FILE*	fp;
691
692
693
694

	xeditnum--;

	if(cfg.xedit[xeditnum]->misc&QUICKBBS) {
695
696
		SAFEPRINTF2(path,"%s%s",cfg.node_dir, cfg.xedit[xeditnum]->misc&XTRN_LWRCASE ? "msginf":"MSGINF");
		removecase(path);
697
698
		if((fp=fopen(path,"wb"))==NULL) {
			errormsg(WHERE,ERR_OPEN,path,O_WRONLY|O_CREAT|O_TRUNC);
699
700
			return; 
		}
701
		fprintf(fp,"%s\r\n%s\r\n%s\r\n%u\r\n%s\r\n%s\r\n"
702
			,mode&WM_ANON ? text[Anonymous]:from,to,subj,1
703
			,mode&WM_NETMAIL ? "NetMail"
704
				:mode&WM_EMAIL ? "Electronic Mail"
705
706
					:subnum==INVALID_SUB ? nulstr
						:cfg.sub[subnum]->sname
707
			,mode&WM_PRIVATE ? "YES":"NO");
708
709
710
711
712
		/* the 7th line (the tag-line file) is a Synchronet extension, for SlyEdit */
		if((mode&WM_EXTDESC)==0 && tagfile!=NULL)
			fprintf(fp,"%s", tagfile);
		fprintf(fp,"\r\n");
		fclose(fp);
713
	}
714
	else {
715
716
717
		SAFEPRINTF(path,"%sresult.ed",cfg.node_dir);
		removecase(path);
		SAFEPRINTF2(path,"%s%s",cfg.node_dir,cfg.xedit[xeditnum]->misc&XTRN_LWRCASE ? "editor.inf" : "EDITOR.INF");
718
719
720
		removecase(path);
		if((fp=fopen(path,"wb"))==NULL) {
			errormsg(WHERE,ERR_OPEN,path,O_WRONLY|O_CREAT|O_TRUNC);
721
722
			return; 
		}
723
		fprintf(fp,"%s\r\n%s\r\n%u\r\n%s\r\n%s\r\n%u\r\n"
724
725
726
			,subj
			,to
			,useron.number
727
			,mode&WM_ANON ? text[Anonymous]:from
728
729
			,useron.name
			,useron.level);
730
		fclose(fp);
731
	}
732
733
734
735
736
737
738
739
740
741
}



/****************************************************************************/
/* Removes from file 'str' every LF terminated line that starts with 'str2' */
/* That is divisable by num. Function skips first 'skip' number of lines    */
/****************************************************************************/
void sbbs_t::removeline(char *str, char *str2, char num, char skip)
{
742
	char*	buf;
743
744
745
746
747
748
749
    char    slen;
    int     i,file;
	long	l=0,flen;
    FILE    *stream;

	if((file=nopen(str,O_RDONLY))==-1) {
		errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
750
751
		return; 
	}
752
	flen=(long)filelength(file);
753
	slen=strlen(str2);
deuce's avatar
deuce committed
754
	if((buf=(char *)malloc(flen))==NULL) {
755
756
		close(file);
		errormsg(WHERE,ERR_ALLOC,str,flen);
757
758
		return; 
	}
759
760
761
	if(lread(file,buf,flen)!=flen) {
		close(file);
		errormsg(WHERE,ERR_READ,str,flen);
762
		free(buf);
763
764
		return; 
	}
765
766
767
768
	close(file);
	if((stream=fnopen(&file,str,O_WRONLY|O_TRUNC))==NULL) {
		close(file);
		errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_TRUNC);
769
		free(buf);
770
771
		return; 
	}
772
773
774
	for(i=0;l<flen && i<skip;l++) {
		fputc(buf[l],stream);
		if(buf[l]==LF)
775
776
			i++; 
	}
777
778
779
780
	while(l<flen) {
		if(!strncmp((char *)buf+l,str2,slen)) {
			for(i=0;i<num && l<flen;i++) {
				while(l<flen && buf[l]!=LF) l++;
781
782
783
				l++; 
			} 
		}
784
785
786
		else {
			for(i=0;i<num && l<flen;i++) {
				while(l<flen && buf[l]!=LF) fputc(buf[l++],stream);
787
788
789
790
				fputc(buf[l++],stream); 
			} 
		} 
	}
791
	fclose(stream);
792
	free((char *)buf);
793
794
795
796
797
798
}

/*****************************************************************************/
/* The Synchronet editor.                                                    */
/* Returns the number of lines edited.                                       */
/*****************************************************************************/
799
ulong sbbs_t::msgeditor(char *buf, const char *top, char *title)
800
{
801
	int		i,j,line,lines=0,maxlines;
802
	char	strin[TERM_COLS_MAX + 1];
803
	char 	tmp[512];
804
	str_list_t str;
805

rswindell's avatar
rswindell committed
806
807
808
809
810
	if(cols < 2) {
		errormsg(WHERE, ERR_CHK, "columns", cols);
		return 0;
	}

811
812
	rioctl(IOCM|ABORT);
	rioctl(IOCS|ABORT); 
813
814
815

	maxlines=cfg.level_linespermsg[useron.level];

816
	if((str = strListSplit(NULL, buf, "\r\n")) == NULL) {
817
		errormsg(WHERE,ERR_ALLOC,"msgeditor",sizeof(char *)*(maxlines+1));
818
819
		return(0); 
	}
820
821
822
823
	lines = strListCount(str);
	while(lines > maxlines)
		free(str[--lines]);
	str[lines] = NULL;
824
825
826
	if(lines)
		bprintf("\r\nMessage editor: Read in %d lines\r\n",lines);
	bprintf(text[EnterMsgNow],maxlines);
827

828
	if(!menu("msgtabs", P_NOERROR)) {
rswindell's avatar
rswindell committed
829
		for(i=0; i < (cols-1); i++) {
830
			if(i%EDIT_TABSIZE || !i)
831
				outchar('-');
832
833
834
			else 
				outchar('+');
		}
835
836
		CRLF;
	}
837
838
839
	putmsg(top,P_SAVEATR|P_NOATCODES);
	for(line=0;line<lines && !msgabort();line++) { /* display lines in buf */
		putmsg(str[line],P_SAVEATR|P_NOATCODES);
840
841
842
		cleartoeol();  /* delete to end of line */
		CRLF; 
	}
843
	SYNC;
844
	rioctl(IOSM|ABORT);
845
846
847
	while(online) {
		if(line < 0)
			line = 0;
848
		if(line>(maxlines-10)) {
849
			if(line >= maxlines)
850
				bprintf(text[NoMoreLines],line);
851
			else
852
853
				bprintf(text[OnlyNLinesLeft],maxlines-line); 
		}
854
		do {
855
856
857
858
859
			if(str[line] != NULL)
				SAFECOPY(strin, str[line]);
			else
				strin[0]=0;
			if(line < 1)
rswindell's avatar
rswindell committed
860
				carriage_return();
861
862
			getstr(strin, cols-1, K_WRAP|K_MSG|K_EDIT|K_LEFTEXIT|K_NOCRLF);
		} while(console&CON_UPARROW && !line && online);
863

864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
		if(sys_status&SS_ABORT)
			continue;

		if(console&(CON_UPARROW|CON_BACKSPACE)) {
			if(console&CON_BACKSPACE && strin[0] == 0) {
				strListRemove(&str, line);
				for(i = line; str[i]; i++) {
					putmsg(str[i], P_SAVEATR|P_NOATCODES);
					cleartoeol();
					newline();
				}
				clearline();
				cursor_up(i - line);
				continue;
			} else if(str[line] == NULL) {
				if(strin[0] != 0)
					strListAppend(&str, strin, line);
			} else
				strListReplace(str, line, strin);
			if(line < 1)
				continue;
			carriage_return();
			cursor_up();
			cleartoeol();
			line--; 
889
			continue;
890
		}
891
892
893
894
		if(console&CON_DELETELINE) {
			strListDelete(&str, line);
			continue;
		}
895
896
897
898
899
900
901
902
		newline();
		if(console&CON_DOWNARROW) {
			if(str[line] != NULL) {
				strListReplace(str, line, strin);
				line++;
				continue;
			}
		}
903
904
		if(strin[0]=='/' && strlen(strin)<8) {
			if(!stricmp(strin,"/DEBUG") && SYSOP) {
905
				bprintf("\r\nline=%d lines=%d (%d), rows=%ld\r\n",line,lines,(int)strListCount(str), rows);
906
				continue;
907
			}
908
			else if(!stricmp(strin,"/ABT")) {
909
				strListFree(&str);
910
				return(0);
911
			}
912
913
			else if(toupper(strin[1])=='D') {	/* delete a line */
				if(str[0] == NULL)
914
					continue;
915
				lines = strListCount(str);
916
917
918
919
920
921
				i=atoi(strin+2)-1;
				if(i==-1)   /* /D means delete last line */
					i=lines-1;
				if(i>=lines || i<0)
					bputs(text[InvalidLineNumber]);
				else {
922
					free(str[i]);
923
924
925
					lines--;
					while(i<lines) {
						str[i]=str[i+1];
926
927
						i++; 
					}
928
					str[i] = NULL;
929
					if(line>lines)
930
931
932
933
						line=lines; 
				}
				continue; 
			}
934
935
936
			else if(toupper(strin[1])=='I') {	/* insert a line before number x */
				lines = strListCount(str);
				if(line >= maxlines || !lines)
937
938
					continue;
				i=atoi(strin+2)-1;
939
940
941
				if(i < 0)
					i = lines - 1;
				if(i >= lines || i < 0)
942
943
					bputs(text[InvalidLineNumber]);
				else {
944
					strListInsert(&str, "", i);
945
946
					line=++lines; 
				}
947
				continue;
948
			}
949
950
			else if(toupper(strin[1])=='E') {	/* edit a line */
				if(str[0] == NULL)
951
					continue;
952
				lines = strListCount(str);
953
954
955
956
				i=atoi(strin+2)-1;
				j=K_MSG|K_EDIT; /* use j for the getstr mode */
				if(i==-1) { /* /E means edit last line */
					i=lines-1;
957
958
					j|=K_WRAP;	/* wrap when editing last line */
				}    
959
960
961
				if(i>=lines || i<0)
					bputs(text[InvalidLineNumber]);
				else
rswindell's avatar
rswindell committed
962
					getstr(str[i], cols-1 ,j);
963
964
				continue; 
			}