1 /*
2 ATP QWK MAIL READER FOR READING AND REPLYING TO QWK MAIL PACKETS.
3 Copyright (C) 1992, 1993, 1997 Thomas McWilliams
4 Copyright (C) 1990 Rene Cougnenc
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /*
22 reply.c
23 */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31
32 #include "reader.h"
33 #include "readlib.h"
34 #include "ansi.h"
35 #include "makemail.h"
36 #include "qlib.h"
37
38 /************************* begin Reply() routines *********************/
39
40 /* size for reply prompt buffer */
41 #define RPMSIZE 300
42
43 #if defined(HAVE_LIBTERMCAP)
44 /*
45 * do_tc_prompt, called by reply_prompt() on termcap based systems.
46 */
47 static void
do_tc_prompt(char * rpmbuf,const int subject_length,const char * p)48 do_tc_prompt(char *rpmbuf, const int subject_length, const char *p)
49 { /*@-strictops */
50 if (*cur_right != NUL_CHAR) { /*@=strictops */
51 char *t;
52 int k, i, buf_leng = 0;
53 strcat(rpmbuf, "[ ");
54 t = tputs_ptr(cur_right);
55 for (i = 0; i < subject_length; i++)
56 strcat(rpmbuf, t);
57 strcat(rpmbuf, " ]");
58 buf_leng = strlen(rpmbuf);
59 for (i = 0; i < RPMSIZE; i++)
60 rpmbuf[buf_leng + i] = (char) SPC_CHAR;
61 rpmbuf[buf_leng] = NUL_CHAR;
62 strcat(rpmbuf, "\r");
63 k = strlen(p) + 2;
64 for (i = 0; i < k; i++)
65 strcat(rpmbuf, t);
66 }
67 }
68 #endif
69
70 static const char *prm_ansi[] =
71 {"error", PRMTTO, PRMTFR, PRMTSB, PRMTSL, PRMTSC, NULL};
72
73 enum rep_prompt_type {
74 REPERR = 0, REPTO, REPFR, REPSB, REPSBL, REPSEC
75 };
76
77 /*
78 * reply_prompt, create prompts for user when entering a message.
79 *
80 * modes for reply prompts: to, from, subject, long_subject, security.
81 */
82 static char *
reply_prompt(enum rep_prompt_type rpm)83 reply_prompt(enum rep_prompt_type rpm)
84 {
85 static char rpmbuf[RPMSIZE];
86 const char *p;
87 rpmbuf[0] = NUL_CHAR;
88 strcat(rpmbuf, "\r");
89 if (ansi) {
90 strcat(rpmbuf, prm_ansi[(unsigned)rpm]);
91 } else {
92 #if defined(HAVE_LIBTERMCAP)
93 int subject_length = SHORT_SUBJ_LEN;
94 #endif
95 switch (rpm) {
96 case REPTO:
97 p = PRTO;
98 break;
99 case REPFR:
100 p = PFROM;
101 break;
102 case REPSB:
103 p = PSUBJ;
104 break;
105 case REPSBL:
106 p = PSUBJ;
107 #if defined(HAVE_LIBTERMCAP)
108 subject_length = LSUBJ_BODY_LEN;
109 #endif
110 break;
111 case REPSEC:
112 p = PSECR;
113 break;
114 default:
115 /* switch() case error */
116 error_switch_case((int)rpm, __FILE__, __LINE__);
117 p = " ERROR in reply_prompt() ";
118 }
119 strcat(rpmbuf, p);
120 #if defined(HAVE_LIBTERMCAP)
121 do_tc_prompt(rpmbuf, subject_length, p);
122 #endif
123 }
124 return rpmbuf;
125 }
126
127
128 /*
129 * ask_speller, asks user if reply needs spelling check.
130 */
131 static atp_ERROR_T
ask_spell(const reply_type_t mode,const char * fname)132 ask_spell(const reply_type_t mode, const char *fname)
133 {
134 atp_ERROR_T ret_code = ATP_OK;
135 char CONSPTR speller = get_atprc_str(spellr);
136 char *intmp; /*@-strictops */
137
138 if (mode != XPOST && speller[0] != NUL_CHAR ) { /*@=strictops */
139 do {
140 intmp = readline("Check spelling [Y/n] ?", do_scroll);
141 } while (intmp == NULL);
142 StripDel(intmp);
143 intmp[0] = tolower(intmp[0]); /*@-strictops */
144 if (intmp[0] != 'n') { /*@=strictops */
145 fork_execvp(speller, fname);
146 restore_terminal(); /* restore terminal parameters */
147 #ifdef USE_FORK
148 if (get_reperror())
149 ret_code = ATP_ERROR;
150 #endif
151 }
152 free_string(intmp);
153 }
154 free_string(speller);
155 return ret_code;
156 }
157
158
159 /*
160 * ask_user, confirm disposition of reply with the user.
161 * bufr *must* point to a buffer
162 */
163 static void
ask_user(const reply_type_t mode,char * bufr)164 ask_user(const reply_type_t mode, char *bufr)
165 { /*@-strictops */
166 if (mode == XPOST) /*@=strictops */
167 bufr[0] = 'x';
168 else
169 for (;;) {
170 char *intmp;
171 CLSCRN();
172 printf("\n");
173 if (get_tag_flag()) {
174 strcpy(bufr, "tag ?");
175 Tag(bufr);
176 printf("Fido style tagline is %s.\n\n", fido ? "ON" : "OFF");
177 } else {
178 printf("\nTaglines are currently disabled.\n");
179 printf("Type \"tag on\" to enable them.\n");
180 }
181 printf("select:\n\t`Save' to save reply.\n");
182 printf("\t`Edit' to re-edit reply.\n");
183 printf("\t`Fido' to toggle tagline style and save reply.\n");
184 printf("\t`Tag' allows you to change tagline (see ATP man page)\n");
185 printf("\t`Abort' to cancel reply.\n\n");
186 do {
187 intmp = readline("Choose: Save, Edit, Fido, Tag, Abort [Save] ? ", do_scroll);
188 } while (intmp == NULL);
189 strcpy(bufr, intmp);
190 free_string(intmp);
191 StripDel(bufr);
192 StripLSpace(bufr);
193 bufr[0] = tolower(bufr[0]); /*@-strictops */
194 if (bufr[0] == NUL_CHAR || bufr[0] == 's' || bufr[0] == 'f' || bufr[0] == 'a' || bufr[0] == 'e')
195 break; /*@=strictops */
196 if (strnicmp(bufr, "tag", (size_t)3) == 0) {
197 /* call tagline routines */
198 tag_set_edit_reply_mode(TRUE);
199 Tag(bufr);
200 tag_set_edit_reply_mode(FALSE);
201 }
202 }/* end of for(;;) loop */
203 }
204
205
206 /*
207 * scpy, copies len bytes to src while also deleting left spaces.
208 */
209 static void
scpy(char * dest,const char * src,const size_t len)210 scpy(char *dest, const char *src, const size_t len)
211 {
212 int i = (int) len ;
213 (void) memcpy( dest, src, len);
214 do {
215 *(dest + i) = NUL_CHAR;
216 i--; /*@-strictops */
217 } while ( 0 <= i && *(dest + i) == SPC_CHAR); /*@=strictops */
218 }
219
220 /*
221 * set_lowcase, subroutine for get_from().
222 */
223 static atp_BOOL_T
set_lowcase(const reply_type_t mode,char * Header)224 set_lowcase(const reply_type_t mode, char *Header)
225 {
226 atp_BOOL_T lowcase = fido; /* True or False boolean valued */
227 /*@-strictops */
228 if (mode == REPLY) {
229 char CONSPTR sl_tmp = Header + HForWhom;
230 size_t i = 0;
231 lowcase = FALSE;
232 for (; i < field_len; i++)
233 if (sl_tmp[i] >= 'a' && sl_tmp[i] <= 'z')
234 lowcase = TRUE;
235 } else if (mode == ENTER) { /*@=strictops */
236 /* PCBoard likes uppercase to/from field */
237 lowcase = get_caps() ? FALSE : TRUE ;
238 }
239 return lowcase;
240 }
241
242 /*
243 * error_too_long, prints message if author or recipient address is too long.
244 */
245 static void
error_too_long(void)246 error_too_long(void)
247 {
248 red();
249 printf(" %s", txt[34]);
250 green();
251 printf("\n");
252 }
253
254 /*
255 * error_msg_aborted, message printed when reply is aborted.
256 */
257 static void
error_msg_aborted(void)258 error_msg_aborted(void)
259 {
260 blue();
261 printf(" %s.", txt[33]);
262 green();
263 printf("\n");
264 }
265
266
267 static char tmp[MAXPATHS], dummy[MAXPATHS];
268 /*
269 * get_from, get who the message is from.
270 */
271 static atp_ERROR_T
get_from(const reply_type_t mode,char * Qmail)272 get_from(const reply_type_t mode, char *Qmail)
273 {
274 atp_ERROR_T ret_code;
275 char CONSPTR UserName = get_cntrl_str(usrnm);
276 assert(UserName != NULL);
277 printf("\n");
278 for (;;) {
279 atp_BOOL_T lowcase;
280 char CONSPTR Header = rbuf;
281 char *intmp = NULL;
282 green();
283 strcpy(dummy, UserName);
284 luxptr = dummy; /*@-strictops */
285 if (mode == CHANGE || mode == XPOST) { /*@=strictops */
286 scpy(dummy, Header + HAuthor, field_len);
287 }
288 lowcase = set_lowcase(mode, Header);
289 if (!lowcase)
290 (void)strupr(dummy);
291 do {
292 intmp = readline(reply_prompt(REPFR), do_scroll);
293 } while (intmp == NULL);
294 strcpy(tmp, intmp);
295 add_history(intmp);
296 free_string(intmp);
297 luxptr = NULL;
298 StripDel(tmp);
299 if (strlen(tmp) <= field_len)
300 break;
301 /* "Entry too long! field_len chars max */
302 error_too_long();
303 printf("\n");
304 }
305 free_string(UserName); /*@-strictops */
306 if (tmp[0] != NUL_CHAR) { /*@=strictops */
307 /* write name in the from field */
308 str2mem(Qmail + HAuthor, tmp);
309 ret_code = ATP_OK;
310 } else {
311 ret_code = ATP_ERROR;
312 /* message aborted! */
313 error_msg_aborted();
314 }
315 return ret_code;
316 }
317
318 /*
319 * get_to, get who the message is being sent to.
320 */
321 static atp_ERROR_T
get_to(const reply_type_t mode,char * Qmail)322 get_to(const reply_type_t mode, char *Qmail)
323 {
324 atp_ERROR_T ret_code;
325 for (;;) {
326 char *intmp = NULL;
327 char CONSPTR Header = rbuf;
328 green();
329 luxptr = dummy; /*@-strictops */
330 if (mode == CHANGE || mode == XPOST)
331 scpy(dummy, Header + HForWhom, field_len);
332 else if (mode != ENTER)
333 scpy(dummy, Header + HAuthor, field_len);
334 else
335 strcpy(dummy, "All");
336 if (!fido || (get_caps() && (mode == ENTER))) /* PCBoard likes uppercase to/from field */
337 (void)strupr(dummy);
338 do {
339 intmp = readline(reply_prompt(REPTO), do_scroll);
340 } while (intmp == NULL);
341 if (!fido || (get_caps() && (mode == ENTER))) /* PCBoard likes uppercase to/from field */
342 (void)strupr(intmp); /*@=strictops */
343 strcpy(tmp, intmp);
344 add_history(intmp);
345 free_string(intmp);
346 luxptr = NULL;
347 StripDel(tmp);
348 if (strlen(tmp) <= field_len)
349 break;
350 /* "Entry too long! field_len chars max */
351 error_too_long();
352 printf("\n");
353 }
354 /*@-strictops */
355 if (stricmp(tmp, "N") == SUCCESS || tmp[0] == NUL_CHAR ) { /*@=strictops */
356 ret_code = ATP_ERROR;
357 /* "message aborted" */
358 error_msg_aborted();
359 } else {
360 ret_code = ATP_OK;
361 str2mem(Qmail + HForWhom, tmp);
362 }
363 return ret_code;
364 }
365
366 /* Long subject for PCBoard */
367 static char RepSubjBuf[100];
368
369 /*
370 * get_reply_lsubj - strdups the reply long subject buffer.
371 */
372 char *
get_reply_lsubj(void)373 get_reply_lsubj(void)
374 {
375 char *sb = NULL; /*@-strictops */
376 if (RepSubjBuf[0] != NUL_CHAR ) { /*@=strictops */
377 sb = strdup(RepSubjBuf);
378 test_fatal_malloc(sb, __FILE__, __LINE__);
379 }
380 return sb;
381 }
382
383
384 /*
385 * setup_long_subject, initializes reply subject buffer.
386 */
387 static void
setup_long_subject(const char * Header)388 setup_long_subject(const char *Header)
389 {
390 if (PCBLONG)
391 Check4LongSubj();
392 if (subj_is_long()) {
393 char CONSPTR tp = get_long_subj();
394 assert( tp != NULL );
395 strcpy(RepSubjBuf, tp);
396 free_string(tp);
397 } else {
398 scpy(RepSubjBuf, Header + HSubject, field_len);
399 }
400 }
401
402 /*
403 * get_subject, get subject of the message.
404 */
405 static atp_ERROR_T
get_subj(const reply_type_t mode,char * Qmail)406 get_subj(const reply_type_t mode, char *Qmail)
407 {
408 atp_ERROR_T ret_code;
409 char *intmp = NULL;
410 char CONSPTR Header = rbuf;
411 green(); /*@-strictops */
412 if (mode != ENTER) { /*@=strictops */
413 setup_long_subject(Header);
414 luxptr = RepSubjBuf;
415 }
416 do {
417 intmp = readline(reply_prompt((PCBLONG ? REPSBL : REPSB)), do_scroll);
418 } while (intmp == NULL);
419 strcpy(tmp, intmp);
420 add_history(intmp);
421 free_string(intmp);
422 /* remember that luxptr is global so re-initialize ! */
423 luxptr = NULL;
424 StripDel(tmp); /*@-strictops */
425 if (stricmp(tmp, "N") == SUCCESS || tmp[0] == NUL_CHAR ) { /*@=strictops */
426 /* "message aborted" */
427 error_msg_aborted();
428 ret_code = ATP_ERROR;
429 } else {
430 ret_code = ATP_OK;
431 /* length of long subject line */
432 if (strlen(tmp) > (size_t) LSUBJ_BODY_LEN)
433 tmp[LSUBJ_BODY_LEN] = NUL_CHAR;
434 /* make sure that it fits header field */
435 if (strlen(tmp) > (size_t) SHORT_SUBJ_LEN) {
436 strcpy(RepSubjBuf, tmp);
437 tmp[SHORT_SUBJ_LEN] = NUL_CHAR;
438 } else {
439 /* don't use long subject if length <= field_len */
440 RepSubjBuf[0] = NUL_CHAR;
441 }
442 str2mem(Qmail + HSubject, tmp);
443 }
444 return ret_code;
445 }
446
447 /*
448 * get_security, get message security: public or private.
449 */
450 static atp_ERROR_T
get_security(const reply_type_t mode,char * Qmail)451 get_security(const reply_type_t mode, char *Qmail)
452 {
453 atp_ERROR_T ret_code;
454 char CONSPTR Header = rbuf;
455 for (;;) {
456 char *intmp = NULL;
457 green(); /*@-strictops */
458 if ((mode != ENTER) &&
459 (Header[HStatus] == '+' || Header[HStatus] == '*')){/*@=strictops */
460 luxptr = "Receiver Only (private)";
461 add_history("None (public)");
462 } else {
463 luxptr = "None (public)";
464 add_history("Receiver Only (private)");
465 }
466 do {
467 intmp = readline(reply_prompt(REPSEC), do_scroll);
468 } while (intmp == NULL);
469 strcpy(tmp, intmp);
470 add_history(intmp);
471 free_string(intmp);
472 luxptr = NULL;
473 StripDel(tmp);
474 StripLSpace(tmp);
475 if (strlen(tmp) <= field_len)
476 break;
477 red();
478 printf("%s", txt[35]); /* "Too long! R=...etc..." */
479 green();
480 printf("\n");
481 } /*@-strictops */
482 if (tmp[0] != NUL_CHAR) {
483 (void)strupr(tmp);
484 if (tmp[0] == 'R' || strnicmp(tmp,"priv", (size_t)4) == SUCCESS ) /*@=strictops */
485 Qmail[HStatus] = (char) PRIVATE_MSG;
486 else
487 Qmail[HStatus] = (char) PUBLIC_MSG;
488 ret_code = ATP_OK;
489 } else {
490 ret_code = ATP_ERROR;
491 /* "message aborted" */
492 error_msg_aborted();
493 }
494 return ret_code;
495 }
496
497 /* index into array */
498 static int TargetConfIdx;
499 /*
500 * get_address_fin, finishes building message header for replies.
501 */
502 static void
get_address_fin(const reply_type_t mode,char * Qmail)503 get_address_fin(const reply_type_t mode, char *Qmail)
504 {
505 char CONSPTR Header = rbuf;
506 /* Msg goes to the target conference */
507 sprintf(dummy, "%-7d", ConfNumbers[TargetConfIdx]);
508 str2mem(Qmail + HConfNum, dummy);
509
510 /* get reference number if available */ /*@-strictops */
511 if (mode == CHANGE )
512 scpy(dummy, Header + HRefMsg, (size_t)7);
513 else if (mode == ENTER || mode == XPOST) /*@=strictops */
514 dummy[0] = NUL_CHAR;
515 else
516 scpy(dummy, Header + HNumMsg, (size_t)7);
517 str2mem(Qmail + HRefMsg, dummy);
518 }
519
520
521 static const char *months[] =
522 {"none", "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
523 "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
524 static const char *monums[] =
525 {"00-", "01-", "02-", "03-", "04-", "05-", "06-",
526 "07-", "08-", "09-", "10-", "11-", "12-"};
527
528 /*
529 * le date et l'heure d'entete du message.
530 * tetedate, returns a pointer to the reply date/time string.
531 */
532 static char *
tetedate(void)533 tetedate(void)
534 {
535 int i;
536 time_t t;
537 char tbuf[30];
538 static char tbuf2[30];
539 t = time(NULL);
540 tzset();
541 /* this macro is used only by msdos and its relatives */
542 ADJUST_DOS_TIME
543 sprintf(tbuf, "%s", ctime(&t));
544 tbuf[7] = NUL_CHAR;
545 tbuf[10] = NUL_CHAR;
546 tbuf[16] = NUL_CHAR;
547 tbuf[21] = '-';
548 tbuf[24] = NUL_CHAR;
549 /* default case */
550 sprintf(tbuf2, "01-01-7001:00");
551 for (i = 1; i < 13; i++) {
552 const int found_a_match = stricmp(tbuf+4, months[i]);
553 if (found_a_match == SUCCESS) {
554 sprintf(tbuf2, "%s%s%s%s", monums[i], tbuf+8, tbuf+21, tbuf+11);
555 break;
556 }
557 } /*@-strictops */
558 if (tbuf2[3] == SPC_CHAR) /*@=strictops */
559 tbuf2[3] = '0';
560 return tbuf2;
561 }
562
563 /*
564 * Remise du Header aux param�tres par d�faut
565 * ResetHeader, put the default parameters in the reply header.
566 */
567 static void
ResetHeader(char * Qmail)568 ResetHeader(char *Qmail)
569 {
570 const unsigned high_byte = 0xff00;
571 const unsigned low_byte = 0x00ff;
572 byte CONSPTR ptr = (byte *) Qmail;
573 byte word16[2]; /* space for 16 bit unsigned word */
574 char qbuf[30]; /* temporary buffer */
575
576 /* Given index number, retrieve real conference number */
577 const unsigned RealConfN = (unsigned)ConfNumbers[TargetConfIdx];
578 #ifdef ATPDBG
579 const int temp = ConfNumbers[TargetConfIdx];
580 assert( -1 < temp );
581 #endif
582
583 /* fill new header with spaces ; struct remplie d'espaces */
584 (void)memset((void *) Qmail, (int)SPC_CHAR, header_SIZE);
585
586 /* Mark message as "public message, unread" */
587 Qmail[HStatus] = PUBLIC_MSG;
588
589 /* Add ascii conference number to header */
590 sprintf(qbuf, "%-7d", ConfNumbers[TargetConfIdx]);
591 str2mem(Qmail + HConfNum, qbuf);
592
593 /* Now enter the time as formatted by a call to tetedate() */
594 str2mem(Qmail + HMsgDate, tetedate());
595
596 /* create a 16bit little endian unsigned conference number */
597 word16[0] = (byte) (RealConfN & low_byte );
598 word16[1] = (byte) ((RealConfN & high_byte ) >> 8);
599 (void)memcpy((void *) &Qmail[HBinConfN], (void *) word16, (size_t) 2);
600
601 /* Mark message as "active", 0xE1 = active 0xE2 = non active */
602 *(ptr + HMsgActiv) = ACTIVE_MSG;
603 *(ptr + HUnused1) = SPC_CHAR; /* space */
604 *(ptr + HUnused2) = SPC_CHAR; /* space */
605 *(ptr + HNetTag) = SPC_CHAR; /* space */
606 }
607
608
609 /*
610 * get_address, build subject/author/recepient/security fields for message.
611 */
612 static atp_ERROR_T
get_address(const reply_type_t mode,char * Qmail)613 get_address(const reply_type_t mode, char *Qmail)
614 {
615 atp_ERROR_T ret_code = ATP_ERROR;
616 ResetHeader(Qmail);
617
618 /* get who the message is from */ /*@-strictops */
619 if (get_from(mode, Qmail) == ATP_OK
620
621 /* get who the message is being sent to */
622 && get_to(mode, Qmail) == ATP_OK
623
624 /* get subject of message */
625 && get_subj(mode, Qmail) == ATP_OK
626
627 /* get security of message */
628 && get_security(mode, Qmail) == ATP_OK) {
629
630 /* finish up */
631 get_address_fin(mode, Qmail);
632 ret_code = ATP_OK;
633 }
634 return ret_code;
635 }
636
637
638 /*
639 * AddReply, add the reply to a Qmail reply.msg file.
640 */
641 static void
AddReply(const reply_type_t mode,const char * fname,char * Qmail)642 AddReply(const reply_type_t mode, const char *fname, char *Qmail)
643 {
644 if (OpenRepFile(add_reply) && KodeMessage(fname, Qmail) == ATP_OK) {
645 do_unlink(fname);
646 set_ReplyExist(TRUE, rexist_AddReply);
647 if (autotag)
648 ChooseTag();
649 if (get_saved_conf() == RCONF_IDX) {
650 const long total_messages = get_TotMsg() + 1L ;
651 set_TotMsg(total_messages, tm_AddReply);
652 /* CurConf = RCONF_IDX; */
653 if (mode == CHANGE)
654 ReadNext(NUKE);
655 if (mode == XPOST) /*@=strictops */
656 ReadNext(CROSS);
657 else {
658 sprintf(dummy, "%ld", get_TotMsg());
659 GoToNum(dummy);
660 }
661 }
662 }
663 }
664
665
666
667 /*
668 * Rep_conf_stoi, finds conference integer index based on string argument.
669 *
670 */
671 static int
Rep_conf_stoi(const char * cmdargs)672 Rep_conf_stoi(const char *cmdargs)
673 {
674 int i;
675 if (Numeric(cmdargs)) {
676 i = findCindex(atoi(cmdargs));
677 } else if (stricmp(cmdargs, "MAIN") == SUCCESS) { /* not numeric */
678 i = 0;
679 } else {
680 /* search for match in conf list */
681 const size_t cmdarg_len = strlen(cmdargs);
682 for (i = 0; i <= LastConf; i++)
683 if (strnicmp(cmdargs, ConfNames[i], cmdarg_len) == SUCCESS)
684 break;
685 }
686 return i;
687 }
688
689 static const int not_valid_target = (int)ATP_ERROR ;
690 /*
691 * Rep_valid_range, verifies that a conference index is in the proper range.
692 */
693 static int
Rep_valid_range(const int target)694 Rep_valid_range(const int target)
695 {
696 return ((0 <= target && target < RCONF_IDX) ? target : not_valid_target );
697 }
698
699 static const int no_args = -1 ;
700
701 /*
702 * Rep_mode_valid, verifies that requested Reply() operation is legal.
703 */
704 static atp_BOOL_T
Rep_mode_valid(const reply_type_t mode,const int target)705 Rep_mode_valid(const reply_type_t mode, const int target)
706 {
707 atp_BOOL_T is_valid = FALSE;
708 const int saved_conf = get_saved_conf();
709 assert(-1 < saved_conf);
710 if (target == no_args) {
711 switch (mode) {
712 case XPOST:
713 break;
714 case ENTER:
715 is_valid = (saved_conf != PCONF_IDX && saved_conf != RCONF_IDX) ?
716 TRUE : FALSE;
717 break;
718 case CHANGE:
719 is_valid = (saved_conf == RCONF_IDX) ? TRUE : FALSE;
720 break;
721 case REPLY:
722 is_valid = (saved_conf != RCONF_IDX) ? TRUE : FALSE;
723 break;
724 default:
725 /* switch() case error */
726 error_switch_case((int)mode, __FILE__, __LINE__);
727 }
728 } else
729 is_valid = (Rep_valid_range(target) == not_valid_target) ?
730 FALSE : TRUE;
731 return is_valid;
732 }
733
734
735 /*
736 * Rep_conf_header, read conference number from header, find index and validate.
737 */
738 static int
Rep_conf_header(const char * Header)739 Rep_conf_header(const char *Header)
740 {
741 const int target = findCindex(readCnum(Header + HBinConfN));
742 return Rep_valid_range(target);
743 }
744
745 /*
746 * get_target_conf, get proper conference number to inscribe in reply header.
747 */
748 static int
get_target_conf(const reply_type_t mode)749 get_target_conf(const reply_type_t mode)
750 {
751 int target = not_valid_target;
752 /*@-strictops */
753 if (mode == CHANGE || get_saved_conf() == PCONF_IDX) {
754 /* rbuf points to message header */
755 target = Rep_conf_header(rbuf);
756 } else if (mode == REPLY || mode == ENTER) { /*@=strictops */
757 target = get_saved_conf();
758 } else {
759 fprintf(stderr, "Unknown mode: %d %s:%d\n", (int)mode, __FILE__, __LINE__);
760 (void)sleep(3);
761 }
762 return target;
763 }
764
765 /*
766 * Rep_mode_parse, initializes context for Reply().
767 */
768 static int
Rep_mode_parse(const reply_type_t mode,const char * cmdline)769 Rep_mode_parse(const reply_type_t mode, const char *cmdline)
770 {
771 int target;
772 /* check for command line arguments */
773 const atp_BOOL_T m_args = ((sscanf(cmdline, "%s %s", dummy, tmp)) > 1) ? TRUE : FALSE;
774
775 if (m_args) {
776 /* convert string to number */
777 target = Rep_conf_stoi(tmp);
778
779 /* verify combination of mode and target */
780 if (!Rep_mode_valid(mode, target)) {
781 target = not_valid_target;
782 }
783 } else if (Rep_mode_valid(mode, no_args)) {
784 target = get_target_conf(mode);
785 } else
786 target = not_valid_target;
787 return target;
788 }
789 /* end of mode parse routines */
790
791
792 /*
793 * call_editor, invoke editor for reply.
794 */
795 static atp_ERROR_T
call_editor(const reply_type_t mode,const char * fname)796 call_editor(const reply_type_t mode, const char *fname)
797 {
798 atp_ERROR_T ret_code = ATP_OK;
799 /*@-strictops */
800 if (mode != XPOST) { /*@=strictops */
801 char CONSPTR Editor = get_atprc_str(editr);
802 assert( Editor != NULL );
803 white();
804 /* "calling editor" */
805 printf("\n%s\n", txt[38]);
806 fork_execvp(Editor, fname);
807 free_string(Editor);
808 /* reset terminal attributes */
809 restore_terminal();
810 #ifdef USE_FORK
811 if (get_reperror())
812 ret_code = ATP_ERROR;
813 #endif
814 }
815 return ret_code;
816 }
817
818
819 /*
820 * McAdjust, adjust for Mc prefix as in McWilliams.
821 */
822 ATP_INLINE void
McAdjust(size_t * i,const char ** str,char ** dst,const size_t len)823 McAdjust(size_t *i, const char **str, char **dst, const size_t len)
824 {
825 const char CONSPTR McPrefix = "Mc";
826 const size_t mick_len = (size_t) STRING_LEN("Mc");
827 const size_t too_far = (size_t) (len - mick_len);
828
829 assert( len >= mick_len );
830 if (*i < too_far && strnicmp(((*str) + 1), McPrefix, mick_len) == SUCCESS) {
831 strcpy(((*dst) + 1), McPrefix);
832 (*i) += mick_len;
833 (*dst) += (int) mick_len;
834 (*str) += (int) mick_len;
835 }
836 }
837
838 /*
839 **Formatte le nom en minuscules, avec les initiales Upcase, dans dst, null
840 **terminated.
841 * Initials, build and format author's name for reply headers.
842 */
843 static void
Initials(const char * str,char * dst,const size_t len)844 Initials(const char *str, char *dst, const size_t len)
845 {
846 atp_BOOL_T convert_to_upper = TRUE;
847 size_t i;
848
849 /* build a name with first letter capitalized */
850 for (i = 0; i < len; i++, str++, dst++) {
851 *dst = *str; /*@-strictops */
852 if (*dst == '.' || *dst == '-' || *dst == SPC_CHAR) { /*@=strictops */
853 convert_to_upper = TRUE; /* Next char converted to Upcase */
854 McAdjust(&i, &str, &dst, len); /* adjust for "Mc" prefix */
855 } else if (convert_to_upper) {
856 *dst = toupper(*dst);
857 convert_to_upper = FALSE;
858 } else {
859 *dst = tolower(*dst);
860 }
861 }
862
863 /* build a null-terminated string */
864 do {
865 *dst = NUL_CHAR;
866 dst--; /*@-strictops */
867 } while (*dst == SPC_CHAR); /*@=strictops */
868
869 }
870
871
872 /*
873 * Response modes for RefMessage():
874 * John Doe wrote to all, John Doe wrote to me, John Doe wrote to Guy Beau.
875 */
876 enum ref_hdr_msg {
877 John_Doe_wrote_to_ALL, John_Doe_wrote_to_me, John_Doe_wrote_to_Guy_Beau
878 };
879
880
881 /*
882 * get_response_mode, get relationship of who is writing to whom.
883 */
884 static enum ref_hdr_msg
get_response_mode(const char * Msg)885 get_response_mode(const char *Msg)
886 {
887 enum ref_hdr_msg response_mode;
888 char CONSPTR UserName = get_cntrl_str(usrnm);
889 assert(UserName != NULL);
890
891 if (strnicmp(Msg + HForWhom, "ALL", (size_t) 3) == SUCCESS)
892 response_mode = John_Doe_wrote_to_ALL;
893 else if (strnicmp(Msg + HForWhom, UserName, strlen(UserName)) == SUCCESS)
894 response_mode = John_Doe_wrote_to_me;
895 else
896 response_mode = John_Doe_wrote_to_Guy_Beau;
897 free_string(UserName);
898 return response_mode;
899 }
900
901
902 /*
903 * R�alise l'ent�te de la lettre...
904 * RefMessage, create a message reference string for a reply.
905 */
906 static void
RefMessage(FILE * fp,char * Msg)907 RefMessage(FILE * fp, char *Msg)
908 {
909 if (get_HeadLetter()) {
910 enum ref_hdr_msg response_mode;
911 int mon, day;
912 char Destin[32];
913 char Ref[32];
914
915 Msg[HMsgDate + 2] = '-';
916 Msg[HMsgDate + 5] = '-';
917 sscanf(Msg + HMsgDate, "%d-%d", &mon, &day);
918 Initials(Msg + HAuthor, Destin, field_len);
919 Destin[field_len] = NUL_CHAR;
920 Initials(Msg + HForWhom, Ref, field_len);
921 Ref[field_len] = NUL_CHAR;
922
923 response_mode = get_response_mode(Msg);
924
925 if (strnicmp(Msg + HAuthor, "UUCP", (size_t)4) != SUCCESS ) {
926 /* "Cher" */
927 fprintf(fp, "%s %s,\n", txt[96], Destin);
928 fprintf(fp, "%s", txt[97]);
929 /* "Dans un msg du" */
930 fprintf(fp, " %d %s, ", day, Months[mon - 1]);
931
932 switch (response_mode) {
933 case John_Doe_wrote_to_ALL:
934 /* "vous �crivez" */
935 fprintf(fp, "%s :\n\n", txt[98]);
936 break;
937 case John_Doe_wrote_to_me:
938 /* "vous m'�crivez" */
939 fprintf(fp, "%s :\n\n", txt[99]);
940 break;
941 case John_Doe_wrote_to_Guy_Beau:
942 /* "destin� �" */
943 fprintf(fp, "%s", txt[100]);
944 fprintf(fp, " %s, ", Ref);
945 /* "vous �crivez" */
946 fprintf(fp, "%s :\n\n", txt[98]);
947 break;
948 default:
949 /* switch() case error */
950 error_switch_case((int)response_mode, __FILE__, __LINE__);
951 }
952 }
953 }
954 }
955
956
957
958 /*
959 * output_quoted_line, add quote delimiter character to start of reply line.
960 */
961 static void
output_quoted_line(unsigned long i,byte * ptr,const char * quote,FILE * fq)962 output_quoted_line(unsigned long i, byte * ptr, const char *quote, FILE * fq)
963 {
964 size_t col = strlen(quote);
965 const atp_CODE_T chrset = get_charset();
966 const unsigned long MsgSize = get_MsgSize();
967 fprintf(fq, "%s", quote);
968 while (i < MsgSize) {
969 /*@-strictops */
970 if (*ptr == (byte)'\n') {
971 fprintf(fq, "\n%s", quote);
972 col = strlen(quote);
973 } else if (*ptr != (byte)NUL_CHAR) {
974 if (col < 78) { /* Trunc quoted lines to 78 cols */
975 if (chrset == ISOLAT1)
976 fprintf(fq, "%c", codelu[(unsigned) (*ptr)]);
977 else if (chrset == CHR7BIT)
978 fprintf(fq, "%c", code7bit[(unsigned) (*ptr)]);
979 else
980 fprintf(fq, "%c", *ptr == DOS_SPACE ? SPC_CHAR : *ptr);
981 /*@=strictops */
982 }
983 col++;
984 }
985 i++;
986 ptr++;
987 }
988 }
989
990
991 /*
992 * QuoteDo, open reply file for quoting.
993 */
994 static void
QuoteDo(const char * quote,const char * tmprname)995 QuoteDo(const char *quote, const char *tmprname)
996 {
997 FILE *fq;
998 if ((fq = fopen(tmprname, "w")) == NULL) {
999 /* "Unable to create file" */
1000 printf("%s %s !\n", txt[50], tmprname);
1001 } else {
1002 unsigned long start_of_text_offset = 0L;
1003 char *ptr = rbuf;
1004
1005 /* Dear User, in a message to... */
1006 RefMessage(fq, ptr);
1007
1008 /* point to start of message text */
1009 ptr += header_SIZE;
1010
1011 /* adjust start of message for embedded long subject lines */
1012 if (PCBLONG && subj_is_long()) {
1013 ptr = get_sot(); /* get start_of_text */ /*@-strictops */
1014 start_of_text_offset = (long) (ptr - (rbuf + header_SIZE)); /*@=strictops */
1015 }
1016 /* write quoted lines to file */
1017 output_quoted_line(start_of_text_offset, (byte *) ptr, quote, fq);
1018 fclose(fq);
1019 }
1020 }
1021
1022 /*
1023 * QuoteMsg, copy the current msg in file tmprname with quotes.
1024 */
1025 static void
QuoteMsg(const char * tmprname)1026 QuoteMsg(const char *tmprname)
1027 {
1028 const char CONSPTR Msg = rbuf;
1029 char quote[10];
1030 if (strnicmp(Msg + HAuthor, "SYSOP", (size_t)5) == SUCCESS) {
1031 strcpy(quote, "Sys> ");
1032 } else if (strnicmp(Msg + HAuthor, "UUCP", (size_t)4) == SUCCESS) {
1033 strcpy(quote, "> ");
1034 } else {
1035 int col;
1036 char tmp1[50], tmp2[50], tmp3[50], tmp4[50], tmp5[50];
1037 (void)memcpy(tmp1, Msg + HAuthor, field_len);
1038 tmp1[field_len] = NUL_CHAR;
1039 col = sscanf(tmp1, "%s %s %s %s", tmp2, tmp3, tmp4, tmp5);
1040
1041 assert( 0 <= col && col < 5 );
1042 switch (col) {
1043 case 0:
1044 strcpy(quote, " > ");
1045 break;
1046 case 1: /*@-strictops */
1047 if (tmp2[1] == NUL_CHAR) /*@=strictops */
1048 sprintf(quote, "%c> ", tmp2[0]);
1049 else
1050 sprintf(quote, "%c%c> ", tmp2[0], tmp2[1]);
1051 break;
1052 case 3:
1053 sprintf(quote, "%c%c%c> ", tmp2[0], tmp3[0], tmp4[0]); /* Initials.. */
1054 break;
1055 case 4:
1056 sprintf(quote, "%c%c> ", tmp2[0], tmp5[0]); /* Initials.. */
1057 break;
1058 default:
1059 sprintf(quote, "%c%c> ", tmp2[0], tmp3[0]); /* Initials.. */
1060 }
1061 }
1062 printf("\n%s %s ...\n", txt[59], quote); /* "Quoting message with " */
1063 QuoteDo(quote, tmprname);
1064 }
1065
1066
1067 /*
1068 * do_strip, copy old reply message minus the tagline, called by StripTag().
1069 */
1070 static void
do_strip(byte * ptr,FILE * fq,unsigned long i)1071 do_strip(byte *ptr, FILE * fq, unsigned long i)
1072 {
1073 const unsigned long MsgSize = get_MsgSize();
1074 const atp_CODE_T chrset = get_charset();
1075 while (i < MsgSize) {
1076 /*@-strictops */
1077 if (*ptr == DOS_SPACE) {
1078 fprintf(fq, "\n");
1079 break;
1080 }
1081 if (*ptr == (byte)'\n')
1082 fprintf(fq, "\n");
1083 else if (*ptr == (byte)NUL_CHAR)/* do nothing, i.e. skip the zero character */
1084 ; /* empty statement */
1085 else if (chrset == ISOLAT1)
1086 fprintf(fq, "%c", codelu[(unsigned) (*ptr)]);
1087 else if (chrset == CHR7BIT) /*@=strictops */
1088 fprintf(fq, "%c", code7bit[(unsigned) (*ptr)]);
1089 else
1090 fprintf(fq, "%c", *ptr);
1091 i++;
1092 ptr++;
1093 }
1094 }
1095
1096
1097 /*
1098 * StripTag, strip tagline from re-edited reply.
1099 */
1100 static void
StripTag(const char * tmprname)1101 StripTag(const char *tmprname)
1102 {
1103 FILE *fq;
1104 if ((fq = fopen(tmprname, "w")) == NULL) {
1105 /* "Unable to create file" */
1106 printf("%s %s !\n", txt[50], tmprname);
1107 (void)sleep(4);
1108 } else {
1109 unsigned long i = 0;
1110 byte *ptr = (byte *) (rbuf + header_SIZE);
1111 /* adjust for long embedded subjects */
1112 if (PCBLONG && subj_is_long()) { /*@-strictops */
1113 assert( strlen(RepSubjBuf) > field_len ); /* ??? */
1114 while (*ptr != (byte)LF_CHAR) /*@=strictops */
1115 i++, ptr++;
1116 i++, ptr++; /*@-strictops */
1117 if(*ptr == (byte)LF_CHAR) /*@=strictops */
1118 i++, ptr++;
1119 }
1120 /* setup is complete, now do the actual work */
1121 do_strip(ptr, fq, i);
1122 fflush(fq);
1123 fclose(fq);
1124 }
1125 }
1126
1127
1128 /*
1129 * editpp_fname, pre-process reply file for quoting and tagline removal.
1130 */
1131 static void
editpp_fname(const reply_type_t mode,const char * fname,atp_BOOL_T reedit)1132 editpp_fname(const reply_type_t mode, const char *fname, atp_BOOL_T reedit)
1133 {
1134 /*@-strictops */
1135 if (mode == REPLY && !reedit)
1136 /* Quote original new message */
1137 QuoteMsg(fname);
1138 else if ((mode == CHANGE || mode == XPOST) && !reedit)
1139 /* remove tagline from old message */
1140 StripTag(fname);
1141 /*@=strictops */
1142 }
1143
1144
1145 /*
1146 * edit_fname, wrapper sets up housekeeping for call_editor().
1147 * returns either ATP_OK or ATP_ERROR
1148 */
1149 static atp_ERROR_T
edit_fname(const reply_type_t mode,const char * fname,char * Qmail)1150 edit_fname(const reply_type_t mode, const char *fname, char *Qmail)
1151 {
1152 atp_BOOL_T reedit = FALSE;
1153 atp_ERROR_T ret_code = ATP_ERROR;
1154
1155 do {
1156 /* get subject, author, recepient, and security for message */ /*@-strictops */
1157 if (get_address(mode, Qmail) == ATP_OK) {
1158
1159 /* pre-process reply: quote message or strip tagline */
1160 editpp_fname(mode, fname, reedit);
1161
1162 /* finally, here is where we invoke the editor for the reply */
1163 if (call_editor(mode, fname) == ATP_OK
1164
1165 /* here is where we ask if spelling needs checking */
1166 && ask_spell(mode, fname) == ATP_OK) {
1167 /*@=strictops */
1168 /* query user whether to save, abort or re-edit message */
1169 ask_user(mode, tmp);
1170 switch (tmp[0]) {
1171 case 'e':
1172 /* re-edit message */
1173 reedit = TRUE;
1174 break;
1175 case 'a':
1176 /* abort message */
1177 error_msg_aborted();
1178 do_unlink(fname);
1179 reedit = FALSE;
1180 ret_code = ATP_ERROR;
1181 break;
1182 case 'f':
1183 /* flip tagline style */
1184 /*@-casebreak */ /* yes, we fall-through, no break */
1185 togltag();
1186 default:
1187 reedit = FALSE;
1188 /*@=casebreak */
1189 ret_code = ATP_OK;
1190 /* adjust header for fido or not */
1191 if (!fido) {
1192 int i = 0;
1193 for (; i < 50; i++)
1194 Qmail[HForWhom + i] = toupper(Qmail[HForWhom + i]);
1195 }
1196 }
1197 }
1198 }
1199 } while (reedit);
1200 return ret_code;
1201 }
1202
1203 /* initialize to guaranteed invalid conference index */
1204 static const int conf_is_invalid = -17715;
1205 static int SaveConf = -17715;
1206
1207 /*
1208 * get_saved_conf - returns value of SaveConf variable.
1209 */
1210 int
get_saved_conf(void)1211 get_saved_conf(void)
1212 {
1213 return SaveConf;
1214 }
1215
1216 /*
1217 * Reply - reply to current message; enter message; or re-edit a message.
1218 */
1219 void
Reply(const reply_type_t mode,const char * line)1220 Reply(const reply_type_t mode, const char *line)
1221 {
1222 char *intmp, fname[MAXPATHS];
1223 char Qmail[HDRSIZE];
1224
1225 /* save current conference */
1226 SaveConf = get_CurConf();
1227
1228 /* determine which conference to post message in based on mode */
1229 if ((TargetConfIdx = Rep_mode_parse(mode, line)) != (int)ATP_ERROR) {
1230 #ifdef ATPDBG
1231 assert( 0 <= TargetConfIdx && TargetConfIdx < RCONF_IDX );
1232 #endif
1233 /* create temporary file for reply */
1234 if ((intmp = tmpnam(NULL)) != NULL)
1235 strcpy(fname, intmp);
1236 else
1237 sprintf(fname, "%s%c%s", WorkPath, SEP, "reply.tmp");
1238
1239 /* edit the reply file */ /*@-strictops */
1240 if (edit_fname(mode, fname, Qmail) == ATP_OK) { /*@=strictops */
1241
1242 /* add the reply to the reply conference database */
1243 AddReply(mode, fname, Qmail);
1244 }
1245 }
1246 restore_terminal();
1247 #ifdef USE_FORK
1248 if (get_reperror()) {
1249 CLSCRN();
1250 /* clear error condition */
1251 reset_reperror();
1252 }
1253 #endif
1254 /* restore current conference */
1255 /* CurConf = SaveConf; */
1256 /* invalidate saved conference */
1257 SaveConf = conf_is_invalid;
1258 return;
1259 }
1260 /********************** end of Reply() routines *******************/
1261
1262