1 /*
2  *  MSG.C
3  *
4  *  Written on 10-Jul-94 by John Dennis and released to the public domain.
5  *
6  *  Note: Because the SDM routines are NOT the same (the Msgn are the
7  *  same as the UIDs, so the Msgn's are not contiguous), I basically had
8  *  to create my own API to distance my code from the real API.  (Making
9  *  it easy to integrate other message types).  All msgbase specific
10  *  stuff should be kept within the module that handles it.
11  */
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <time.h>
17 #include <errno.h>
18 #include <assert.h>
19 
20 #if !defined(UNIX) && !defined(SASC)
21 #include <io.h>
22 #endif
23 
24 #if defined(UNIX) || defined(__CYGWIN__)
25 #include <unistd.h>
26 #endif
27 
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 
32 #ifdef __MSC__
33 #include <sys/locking.h>
34 #endif
35 
36 #if !defined(UNIX) && !defined(SASC) && !defined(__CYGWIN__)
37 #include <share.h>
38 #endif
39 
40 #include <smapi/msgapi.h>
41 #include "addr.h"
42 #include "nedit.h"
43 #include "msged.h"
44 #include "memextra.h"
45 #include "date.h"
46 #include "normal.h"
47 #include "unused.h"
48 #include "msg.h"
49 #include "charset.h"
50 
51 static char *cinfbuf = NULL;    /* control info buffer, size BUFLEN */
52 
53 static unsigned long num_msgs;  /* number of messages in msgbase */
54 static unsigned long new = 0;   /* if msg being written is new */
55 
56 static time_t stampToTimeT(struct _stamp *st);
57 static struct _stamp *timeTToStamp(time_t);
58 
59 static struct _minf minf;
60 
61 static MSGA *Ahandle = NULL;     /* area handle */
62 static MSGH *mh = NULL;         /* message handle */
63 static XMSG xmsg;               /* squish message header */
64 
65                                 /* these are used by the JAM routines only */
66 static char *global_text = NULL;
67 static unsigned long global_pos = 0;
68 static unsigned long global_len;
69 static unsigned long global_msgn;
70 static int global_ctrl = 1;
71 
72 static int ready = FALSE, ctrl = TRUE;
73 
74 
75 static UMSGID replyto = 0;      /* to ensure correct uplinks when mxx is used */
76 
77 /* Make Msged work with both stable and current smapi: */
78 #ifndef S_IMODE
79 #define S_IMODE S_IREAD | S_IWRITE
80 #endif
81 
82 /*
83  *  SquishMsgAreaOpen; Scans an area for messages, opening the message
84  *  base and filling the message[] array.
85  */
86 
SquishMsgAreaOpen(AREA * a)87 long SquishMsgAreaOpen(AREA * a)
88 {
89     unsigned long lastread;     /* lastread record */
90     char work[256];             /* path to sql file */
91     int sql;                    /* file handle */
92     unsigned long k = 0;        /* counter */
93     struct stat bstat;          /* stats for sql file */
94     unsigned char buffer[4];    /* for reading and writing the sql */
95 
96 
97     a->scanned = 1;
98     a->last = 1;
99     a->first = 1;
100     a->lastread = 0;
101     a->current = 0;
102     a->status = 0;
103 
104     /* open the msgbase */
105 
106     if (mh != NULL)
107     {
108         MsgCloseMsg(mh);
109         mh = NULL;
110     }
111 
112     if (Ahandle != NULL)
113     {
114         if (SW->squish_lock)
115         {
116             MsgUnlock(Ahandle);
117         }
118 
119         if (MsgCloseArea(Ahandle) == -1)
120         {
121             return 0;
122         }
123     }
124 
125 
126     Ahandle = MsgOpenArea((byte *)a->path, MSGAREA_CRIFNEC,
127                           ((a->msgtype == JAM) ? MSGTYPE_JAM:MSGTYPE_SQUISH));
128     if (Ahandle == NULL)
129     {
130         return 0;
131     }
132 
133     if (SW->squish_lock)
134     {
135         if (MsgLock(Ahandle) == -1)  /* Lock failed - return */
136         {
137             MsgCloseArea(Ahandle);
138             Ahandle = NULL;
139             return 0;
140         }
141     }
142 
143     sprintf(work, "%s.sql", a->path);
144     sql = sopen(work, O_BINARY | O_RDONLY, SH_DENYNO, S_IMODE);
145     if (sql != -1)
146     {
147 #if defined(PACIFIC) || defined(LATTICE)
148         stat(work, &bstat);
149 #else
150         fstat(sql, &bstat);
151 #endif
152         /* we make it big enough */
153         if (bstat.st_size < SW->useroffset * 4)
154         {
155             close(sql);
156             sql = sopen(work, O_BINARY | O_RDWR, SH_DENYNO, S_IMODE);
157             if (sql != -1)
158             {
159 #if defined(PACIFIC) || defined(LATTICE)
160                 stat(work, &bstat);
161 #else
162                 fstat(sql, &bstat);
163 #endif
164                 lastread = 0;
165                 buffer[0] = buffer[1] = buffer[2] = buffer[3] = '\0';
166                 k = bstat.st_size / 4;
167                 lseek(sql, 0L, SEEK_END);
168                 while (SW->useroffset > k)
169                 {
170                     farwrite(sql, buffer, 4);
171                     k++;
172                 }
173             }
174         }
175         else
176         {
177             /* we read the data in */
178             lseek(sql, SW->useroffset * 4, SEEK_SET);
179             if (farread(sql, buffer, 4) == 4)
180             {
181                 lastread = buffer[0] + (((unsigned long)(buffer[1])) << 8) +
182                     (((unsigned long)(buffer[2])) << 16) +
183                     (((unsigned long)(buffer[3])) << 24);
184 
185                 if (CurArea.netmail)
186                 {
187                     a->lastread = MsgUidToMsgn(Ahandle, lastread, UID_PREV);
188                 }
189                 else
190                 {
191                     a->lastread = MsgUidToMsgn(Ahandle, lastread, UID_NEXT);
192                 }
193                 a->current = a->lastread;
194             }
195         }
196         close(sql);
197     }
198 
199     if (Ahandle->type != MSGTYPE_JAM)
200         a->last = MsgHighMsg(Ahandle);
201     else
202         a->last = Ahandle->num_msg; /* work around bug in JAM api */
203 
204     a->status = 1;
205 
206     if (a->last >= 1 && a->current == 0)
207     {
208         a->lastread = 0;
209         a->current = 1;
210     }
211 
212     return a->last;
213 }
214 
215 /*
216  *  SquishMsgReadHeader; Reads in the message header and control
217  *  information for the message.
218  */
219 
SquishMsgReadHeader(unsigned long n,int type)220 msg *SquishMsgReadHeader(unsigned long n, int type)
221 {
222     char path[PATHLEN];
223     msg *m;
224     int i = 0;
225 
226     if (cinfbuf == NULL) cinfbuf = xmalloc(BUFLEN);
227 
228     if (Ahandle == NULL)
229     {
230         return NULL;
231     }
232 
233     if (mh != NULL)
234     {
235         /* if open, close it */
236         MsgCloseMsg(mh);
237     }
238 
239     /* open msg we want to open */
240     mh = MsgOpenMsg(Ahandle, MOPEN_READ, n);
241     if (mh == NULL)
242     {
243         return NULL;
244     }
245 
246     if (MsgReadMsg(mh, &xmsg, 0L, 0L, NULL, BUFLEN, (byte *) cinfbuf) == (dword) - 1)
247     {
248         /* no message header or control info! */
249         MsgCloseMsg(mh);
250         mh = NULL;
251         return NULL;
252     }
253 
254     m = xcalloc(1, sizeof *m);
255 
256     /* basically copy info across to msg */
257     m->msgnum = MsgMsgnToUid(Ahandle, n);
258     m->from.zone = xmsg.orig.zone;
259     m->from.net = xmsg.orig.net;
260     m->from.node = xmsg.orig.node;
261     m->from.point = xmsg.orig.point;
262 
263     m->to.zone = xmsg.dest.zone;
264     m->to.net = xmsg.dest.net;
265     m->to.node = xmsg.dest.node;
266     m->to.point = xmsg.dest.point;
267 
268     m->from.domain = NULL;
269     m->to.domain = NULL;
270 
271     if (xmsg.date_written.date.yr != 0)
272     {
273         m->timestamp = stampToTimeT(&xmsg.date_written);
274         m->time_arvd = stampToTimeT(&xmsg.date_arrived);
275     }
276     else
277     {
278         /* only use this when necesary */
279         memset(path, 0, sizeof path);
280         memcpy(path, xmsg.__ftsc_date, sizeof xmsg.__ftsc_date);
281         m->timestamp = parsedate(path);
282     }
283 
284     m->isto = xcalloc(1, sizeof xmsg.to + 1);
285     m->isfrom = xcalloc(1, sizeof xmsg.from + 1);
286     m->subj = xcalloc(1, sizeof xmsg.subj + 1);
287 
288     memcpy(m->isto, xmsg.to, sizeof xmsg.to);
289     memcpy(m->isfrom, xmsg.from, sizeof xmsg.from);
290     memcpy(m->subj, xmsg.subj, sizeof xmsg.subj);
291 
292     m->attrib.priv = (xmsg.attr & MSGPRIVATE) != 0;
293     m->attrib.crash = (xmsg.attr & MSGCRASH) != 0;
294     m->attrib.rcvd = (xmsg.attr & MSGREAD) != 0;
295     m->attrib.sent = (xmsg.attr & MSGSENT) != 0;
296     m->attrib.attach = (xmsg.attr & MSGFILE) != 0;
297     m->attrib.forward = (xmsg.attr & MSGFWD) != 0;
298     m->attrib.orphan = (xmsg.attr & MSGORPHAN) != 0;
299     m->attrib.killsent = (xmsg.attr & MSGKILL) != 0;
300     m->attrib.local = (xmsg.attr & MSGLOCAL) != 0;
301     m->attrib.hold = (xmsg.attr & MSGHOLD) != 0;
302     m->attrib.direct = (xmsg.attr & MSGXX2) != 0;
303     m->attrib.freq = (xmsg.attr & MSGFRQ) != 0;
304     m->attrib.rreq = (xmsg.attr & MSGRRQ) != 0;
305     m->attrib.rcpt = (xmsg.attr & MSGCPT) != 0;
306     m->attrib.areq = (xmsg.attr & MSGARQ) != 0;
307     m->attrib.ureq = (xmsg.attr & MSGURQ) != 0;
308 
309     m->attrib.lock = (xmsg.attr & MSGLOCKED) != 0;
310 
311     if (xmsg.attr & MSGSCANNED)
312     {
313         m->scanned = 1;
314     }
315 
316     if (type != RD_HEADER_BRIEF)
317     {
318         m->replyto = MsgUidToMsgn(Ahandle, xmsg.replyto, UID_EXACT);
319 
320         while (i < 9)
321         {
322             if (xmsg.replies[i] != 0)
323             {
324                 m->replies[i] = MsgUidToMsgn(Ahandle, xmsg.replies[i], UID_EXACT);
325             }
326             else
327             {
328                 m->replies[i] = 0;
329             }
330             i++;
331         }
332         m->replies[9] = 0;
333     }
334 
335     m->cost = 0;
336     m->times_read = 0;
337     m->text = NULL;
338     m->to.fidonet = 1;
339     m->from.fidonet = 1;
340 
341     if (type == RD_HEADER || type == RD_HEADER_BRIEF)
342     {
343         MsgCloseMsg(mh);
344         mh = NULL;
345     }
346 
347     return m;
348 }
349 
350 /*
351  *  SquishMsgReadText; Reads in the entire message, adds the control
352  *  information to the beginning of the message and continues to return
353  *  the message to the caller line by line (at each subsequent call).
354  *  Basically a conversion of the correspoding Fido function.
355  */
356 
SquishMsgReadText(unsigned long n)357 char *SquishMsgReadText(unsigned long n)
358 {
359     static char *next = NULL;
360     static char *end = NULL;
361     char *t = NULL;
362     char *text = NULL;
363     char eol = '\0';
364     unsigned long i, l;
365     static unsigned long ofs = 0, s = 0;
366 
367     unused(n);
368     if (Ahandle == NULL)
369     {
370         return NULL;
371     }
372 
373     if (cinfbuf == NULL) cinfbuf = xmalloc(BUFLEN);
374 
375     if (next == NULL && s != 0)
376     {
377         /* we are finished */
378         s = ofs = 0;
379         return NULL;
380     }
381 
382     if (s == 0)
383     {
384         /* ready to read in new msg */
385         memset(msgbuf, 0, BUFLEN - 1);
386         next = msgbuf;
387         if (MsgGetCtrlLen(mh) > 0)
388         {
389             /* copy control info from */
390             t = cinfbuf;  /* insert /r's */
391             *(t + (size_t) ((MsgGetCtrlLen(mh)>=BUFLEN) ? (BUFLEN - 1) :
392                              MsgGetCtrlLen(mh))) = '\0';
393             if (*t != '\0')
394             {
395                 *next++ = *t++;
396                 while (*t != '\0' && next - msgbuf < BUFLEN - 2)
397                 {
398                     if (*t == '\01')
399                     {
400                         *next++ = '\r';  /* add a \r to the text */
401                     }
402                     if (next - msgbuf < BUFLEN - 2)
403                     {
404                         *next++ = *t++;
405                     }
406                 }
407                 if (*(next - 1) == '\01')
408                 {
409                     next--;
410                     *(next) = '\0';
411                 }
412                 else
413                 {
414                     if (next - msgbuf < BUFLEN - 2)
415                     {
416                         *next++ = '\r';
417                     }
418                     *next = '\0';  /* terminate string         */
419                 }
420             }
421             next = msgbuf;
422             end = msgbuf + normalize(msgbuf);
423         }
424         else
425         {
426             next = NULL;
427         }
428 
429         s = BUFLEN;
430     }
431 
432     /* return msg a line at a time */
433     if (next == NULL)
434     {
435         i = MsgReadMsg(mh, NULL, ofs, s - 1, (byte *) msgbuf, 0L, NULL);
436         ofs += i;
437         if (i < 1)
438         {
439             s = ofs = 0;
440             next = NULL;
441             return NULL;
442         }
443         next = msgbuf;
444         while (i && *next == '\0')
445         {
446             i--;
447             next++;
448         }
449         normalize(next);
450         end = msgbuf + strlen(msgbuf);
451         if (end < next)
452         {
453             next = end;
454         }
455     }
456 
457     if (end - next == 0)
458     {
459         t = NULL;
460     }
461     else
462     {
463         t = memchr(next, '\n', (int)(end - next));
464     }
465 
466     if (t == NULL)
467     {
468         l = strlen(next);
469         memmove(msgbuf, next, (size_t) (l + 1));
470         i = MsgReadMsg(mh, NULL, ofs, s - l - 1,
471           (byte *)(msgbuf + (size_t) l), 0L, NULL);
472         ofs += i;
473         if (i < 1)
474         {
475             next = NULL;
476             return xstrdup(msgbuf);
477         }
478         *(msgbuf + (size_t) l + (size_t) i) = '\0';
479         end = msgbuf + l + normalize(msgbuf + (size_t) l);
480         next = msgbuf;
481         t = strchr(next, '\n');
482     }
483 
484     if (t != NULL)
485     {
486         eol = *(t + 1);
487         *(t + 1) = '\0';
488     }
489 
490     text = xstrdup(next);
491 
492     if (t != NULL)
493     {
494         *(t + 1) = eol;
495         next = t + 1;
496     }
497     else
498     {
499         next = NULL;
500     }
501 
502     return text;
503 }
504 
505 /*
506  *  SquishMsgWriteHeader; Writes message header to the message base,
507  *  creates new frame if new message, and makes sure links are correct.
508  */
509 
SquishMsgWriteHeader(msg * m,int type)510 int SquishMsgWriteHeader(msg * m, int type)
511 {
512     unsigned long n;
513     int i = 0;
514 
515     n = MsgUidToMsgn(Ahandle, m->msgnum, UID_EXACT);
516 
517     if (Ahandle == NULL)
518     {
519         return FALSE;
520     }
521 
522     if (mh != NULL)  /* close old msg, if left open */
523     {
524         MsgCloseMsg(mh);
525     }
526 
527     if (m->new)
528     {
529         /*
530          *  If new, store current number of messages (for use in
531          *  SquishMsgWriteText, and create a new frame for the new
532          *  message.
533          */
534 
535         num_msgs = MsgGetNumMsg(Ahandle);
536         mh = MsgOpenMsg(Ahandle, MOPEN_CREATE, 0L);
537         if (mh == NULL)
538         {
539             return ERR_OPEN_MSG;
540         }
541         new = TRUE;
542     }
543     else
544     {
545         /* else we open the message to be changed */
546         mh = MsgOpenMsg(Ahandle, MOPEN_RW, n);
547         if (mh == NULL)
548         {
549             return ERR_OPEN_MSG;
550         }
551         new = FALSE;
552     }
553 
554     memset(&xmsg, 0, sizeof xmsg);
555 
556     xmsg.attr = 0;
557     if (m->attrib.priv)
558     {
559         xmsg.attr |= MSGPRIVATE;
560     }
561     if (m->attrib.crash)
562     {
563         xmsg.attr |= MSGCRASH;
564     }
565     if (m->attrib.rcvd)
566     {
567         xmsg.attr |= MSGREAD;
568     }
569     if (m->attrib.sent)
570     {
571         xmsg.attr |= MSGSENT;
572     }
573     if (m->attrib.attach)
574     {
575         xmsg.attr |= MSGFILE;
576     }
577     if (m->attrib.forward)
578     {
579         xmsg.attr |= MSGFWD;
580     }
581     if (m->attrib.orphan)
582     {
583         xmsg.attr |= MSGORPHAN;
584     }
585     if (m->attrib.killsent)
586     {
587         xmsg.attr |= MSGKILL;
588     }
589     if (m->attrib.local)
590     {
591         xmsg.attr |= MSGLOCAL;
592     }
593     if (m->attrib.hold)
594     {
595         xmsg.attr |= MSGHOLD;
596     }
597     if (m->attrib.direct)
598     {
599         xmsg.attr |= MSGXX2;
600     }
601     if (m->attrib.freq)
602     {
603         xmsg.attr |= MSGFRQ;
604     }
605     if (m->attrib.rreq)
606     {
607         xmsg.attr |= MSGRRQ;
608     }
609     if (m->attrib.rcpt)
610     {
611         xmsg.attr |= MSGCPT;
612     }
613     if (m->attrib.areq)
614     {
615         xmsg.attr |= MSGARQ;
616     }
617     if (m->attrib.ureq)
618     {
619         xmsg.attr |= MSGURQ;
620     }
621     if (m->attrib.lock)
622     {
623         xmsg.attr |= MSGLOCKED;
624     }
625 
626     if (new == FALSE)
627     {
628         /*
629          *  If the old message had been scanned, then we make sure that
630          *  the MSGSCANNED bit is set on the way out. New messages get
631          *  this bit stripped.
632          */
633 
634         if (m->scanned && !m->new)
635         {
636             xmsg.attr |= MSGSCANNED;
637         }
638     }
639 
640     if (m->replyto != 0)
641     {
642         /* get the links for replies */
643         xmsg.replyto = MsgMsgnToUid(Ahandle, m->replyto);
644     }
645 
646     for (i = 0; i < 9; i++)
647     {
648         if (m->replies[i] != 0)
649         {
650             xmsg.replies[i] = MsgMsgnToUid(Ahandle, m->replies[i]);
651         }
652     }
653 
654     i = 0;
655 
656     while (m->replies[i] != 0 && i < 9)
657     {
658         i++;
659     }
660 
661     if (i == 9)
662     {
663         i = 8;
664     }
665 
666     if (!m->new && replyto != 0)
667     {
668         xmsg.replies[i] = replyto;
669         replyto = 0;
670     }
671 
672     xmsg.dest.zone = (word) m->to.zone;
673     xmsg.dest.net = (word) m->to.net;
674     xmsg.dest.node = (word) m->to.node;
675     xmsg.dest.point = (word) m->to.point;
676 
677     xmsg.orig.zone = (word) m->from.zone;
678     xmsg.orig.net = (word) m->from.net;
679     xmsg.orig.node = (word) m->from.node;
680     xmsg.orig.point = (word) m->from.point;
681 
682     if (m->isto != NULL)
683     {
684         memcpy(xmsg.to, m->isto, min(sizeof xmsg.to, strlen(m->isto)));
685     }
686 
687     if (m->isfrom != NULL)
688     {
689         memcpy(xmsg.from, m->isfrom, min(sizeof xmsg.from, strlen(m->isfrom)));
690     }
691 
692     if (m->subj != NULL)
693     {
694         memcpy(xmsg.subj, m->subj, min(sizeof xmsg.subj, strlen(m->subj)));
695     }
696 
697     memcpy(xmsg.__ftsc_date, mtime(m->timestamp), 20);
698 
699     xmsg.date_written = *timeTToStamp(m->timestamp);
700 
701     if (m->time_arvd != 0)
702     {
703         xmsg.date_arrived = *timeTToStamp(m->time_arvd);
704     }
705     else
706     {
707         xmsg.date_arrived = xmsg.date_written;
708     }
709 
710     if (type == WR_HEADER || !new)
711     {
712         MsgWriteMsg(mh, FALSE, &xmsg, NULL, 0L, 0L, 0L, NULL);
713         MsgCloseMsg(mh);
714         mh = NULL;
715     }
716 
717     return TRUE;
718 }
719 
720 /*
721  *  strip_whitel; Strips the white spaces from the control info,
722  *  copying it to the msgbuf array while it's at it.  Returns new
723  *  length.
724  */
725 
strip_whitel(void)726 static dword strip_whitel(void)
727 {
728     char *s, *c, *cptr;
729 
730     if (cinfbuf == NULL) cinfbuf = xmalloc(BUFLEN);
731 
732     /* we put it in the cinfbuf, killing any \r & \n's in the process */
733     cptr = cinfbuf;
734     s = msgbuf;
735     c = s + strlen(s) + 1;
736     while (s != c)
737     {
738         /* copy buffer across, ignoring any fluff in the source buffer */
739         switch (*s)
740         {
741         case '\r':
742         case '\n':
743             s++;
744             break;
745         default:
746             *cptr++ = *s++;
747             break;
748         }
749     }
750     *cptr = '\0';
751     return cptr - cinfbuf;
752 }
753 
JamMsgWriteText(char * text,unsigned long msgn,unsigned long mlen)754 int JamMsgWriteText(char *text, unsigned long msgn, unsigned long mlen)
755 {
756     int l;
757 
758     if (global_ctrl)
759     {
760         if (text != NULL && *text != '\01')
761         {
762             global_ctrl = 0;
763         }
764         else
765         {
766             return SquishMsgWriteText(text, msgn, mlen);
767         }
768     }
769 
770 
771     if (global_text == NULL)
772     {
773         global_text = xmalloc(mlen + 1);
774         global_pos  = 0;
775         global_len  = mlen;
776         global_msgn = msgn;
777     }
778 
779     if (text != NULL)
780         l = strlen(text);
781     else
782         l = 0;
783 
784     assert(global_len == mlen);
785     assert(global_pos + l <= mlen);
786 
787     if (l)
788     {
789         memcpy(global_text + global_pos, text, l);
790         global_text[global_pos + l] = '\0';
791         global_pos += l;
792     }
793 
794     return TRUE;
795 }
796 
797 
798 /*
799  *  SquishMsgWriteText; Writes message text (and header if a new
800  *  message), to the message base.  If the message is not new and
801  *  new text is larger, it creates a new frame, while keeping the
802  *  same position in the index.
803  */
804 
SquishMsgWriteText(char * text,unsigned long msgn,unsigned long mlen)805 int SquishMsgWriteText(char *text, unsigned long msgn, unsigned long mlen)
806 {
807     static char *tptr, *c;
808     static unsigned long n = 0;
809     char cz = 0;
810     unsigned long clen;
811 
812     if (Ahandle == NULL)
813     {
814         return FALSE;
815     }
816 
817     if (cinfbuf == NULL) cinfbuf = xmalloc(BUFLEN);
818 
819     if (ready == FALSE)
820     {
821         /* starting on new message; reset pointers */
822         ready = TRUE;
823         tptr = msgbuf;
824         n = MsgUidToMsgn(Ahandle, msgn, UID_EXACT);
825     }
826 
827     if (text == NULL)
828     {
829         if (ctrl)
830         {
831             /* no body in the message */
832             if (new)
833             {
834                 clen = strip_whitel();
835 
836                                 /* we could fix this to not use append. */
837                 MsgWriteMsg(mh, FALSE, &xmsg, NULL, 0L, mlen, clen,
838                   (byte *)cinfbuf);
839                 MsgWriteMsg(mh, TRUE, NULL, (byte *)&cz,
840                   sizeof(char), mlen, 0L, NULL);
841             }
842             else
843             {
844                 mh = MsgOpenMsg(Ahandle, MOPEN_RW, n);
845                 if (mh == NULL)
846                 {
847                     ready = FALSE;
848                     ctrl = TRUE;
849                     new = FALSE;  /* we only change the header */
850                     return FALSE;
851                 }
852                 MsgWriteMsg(mh, FALSE, &xmsg, NULL, 0L, mlen, 0L, NULL);
853             }
854         }
855         else
856         {
857                                 /* I THINK we might also be able to fix this
858                                    not to use append. */
859             MsgWriteMsg(mh, TRUE, NULL, (byte *)&cz, sizeof(char),
860               mlen, 0L, NULL);
861         }
862 
863         if (new)
864         {
865             /*
866              *  Message is a reply - save new number so next header
867              *  written can use it for the uplink number.
868              */
869 
870             if (xmsg.replyto)
871             {
872                 replyto = MsgMsgnToUid(Ahandle, MsgGetNumMsg(Ahandle));
873             }
874             if (num_msgs == MsgGetNumMsg(Ahandle))
875             {
876                 CurArea.messages--;
877             }
878         }
879 
880         new = ready = FALSE;
881         ctrl = TRUE;
882         return TRUE;
883     }
884 
885     if (*text == '\01' && ctrl)
886     {
887         c = text;
888         while (*c != '\0')
889         {
890             /* store the control info */
891             *tptr++ = *c++;
892         }
893         *tptr = '\0';
894     }
895     else
896     {
897         if (*text != '\01' && ctrl)
898         {
899             ctrl = FALSE;
900             clen = strip_whitel();
901             if (!new)
902             {
903                 /* we are modifying a non-new message */
904                 mh = MsgOpenMsg(Ahandle, MOPEN_RW, n);
905                 if (mh == NULL)
906                 {
907                     ready = FALSE;
908                     ctrl = TRUE;
909                     new = FALSE;
910                     return FALSE;
911                 }
912 
913                 if (MsgReadMsg(mh, &xmsg, 0L, 0L, NULL, 0L, NULL) == (dword) - 1)
914                 {
915                     new = FALSE;
916                     ready = FALSE;
917                     ctrl = TRUE;
918                     return FALSE;
919                 }
920 
921                 MsgCloseMsg(mh);  /* copy xmsg information across */
922 
923                 mh = MsgOpenMsg(Ahandle, MOPEN_CREATE, n);
924                 if (mh == NULL)
925                 {
926                     ready = FALSE;
927                     ctrl = TRUE;
928                     new = FALSE;
929                     return FALSE;
930                 }
931 
932                 /* messy, but it works */
933                 MsgWriteMsg(mh, FALSE, &xmsg, (byte *)text,
934                   strlen(text), mlen, clen, (byte *)cinfbuf);
935             }
936             else
937             {
938                                 /* we'd need to intercept this call if we want
939                                    to make it work with JAM api */
940 
941                 MsgWriteMsg(mh, FALSE, &xmsg, (byte *)text,
942                   strlen(text), mlen, clen, (byte *)cinfbuf);
943             }
944         }
945         else
946         {
947                                 /* this does NOT work with JAM api */
948             MsgWriteMsg(mh, TRUE, NULL, (byte *)text, strlen(text),
949               mlen, 0L, NULL);
950         }
951     }
952 
953     return TRUE;
954 }
955 
JamMsgClose(void)956 int JamMsgClose(void)
957 {
958     int rv = TRUE;
959     int srv = TRUE;
960 
961     if (global_text != NULL)
962     {
963         if (SquishMsgWriteText(global_text, global_msgn, global_len) != TRUE)
964             rv = ERR_CLOSE_MSG;
965         xfree(global_text);
966 
967 	global_ctrl = 1;            /* reset! */
968 	global_text = NULL;
969 	global_pos  = 0;
970     }
971 
972     srv = SquishMsgClose();
973     return (rv != TRUE ? rv : srv);
974 }
975 
976 /*
977  *  SquishMsgClose; Closes the message currently opened.
978  */
979 
SquishMsgClose(void)980 int SquishMsgClose(void)
981 {
982     if (mh == NULL)
983     {
984         return TRUE;
985     }
986 
987     if (MsgCloseMsg(mh) == -1)
988     {
989         printf("\n!SquishMsgClose(): Message didn't close, error %ud!\n", msgapierr);
990         exit(-1);
991         return ERR_CLOSE_MSG;
992     }
993     else
994     {
995         ready = FALSE; ctrl = TRUE;
996         mh = NULL;
997         return TRUE;
998     }
999 }
1000 
1001 
1002 /*
1003  * Area locking functions
1004  */
1005 
SquishMsgLock(void)1006 int SquishMsgLock(void)
1007 {
1008     if (!SW->squish_lock)
1009     {
1010         return MsgLock(Ahandle);
1011     }
1012     else
1013     {
1014         return 0;  /* area already locked */
1015     }
1016 }
1017 
SquishMsgUnlock(void)1018 int SquishMsgUnlock(void)
1019 {
1020     if (!SW->squish_lock)
1021     {
1022         return MsgUnlock(Ahandle);
1023     }
1024     else
1025     {
1026         return 0;
1027     }
1028 }
1029 
1030 
1031 /*
1032  *  SquishMsgAreaClose; Closes the area currently opened.
1033  */
1034 
SquishMsgAreaClose(void)1035 int SquishMsgAreaClose(void)
1036 {
1037     if (Ahandle == NULL)
1038     {
1039         return TRUE;
1040     }
1041 
1042     if (SW->squish_lock)
1043     {
1044         MsgUnlock(Ahandle);
1045     }
1046 
1047     if (MsgCloseArea(Ahandle) == -1)
1048     {
1049         printf("\n!SquishMsgAreaClose(): Area didn't close, error %ud!\n", msgapierr);
1050         exit(-1);
1051         return ERR_CLOSE_AREA;
1052     }
1053     else
1054     {
1055         CurArea.status = 0;
1056         Ahandle = NULL;
1057         return TRUE;
1058     }
1059 }
1060 
1061 /*
1062  *  SquishUidToMsgn; Returns the corresponding message number of a UID.
1063  */
1064 
SquishUidToMsgn(unsigned long n)1065 unsigned long SquishUidToMsgn(unsigned long n)
1066 {
1067     return MsgUidToMsgn(Ahandle, n, UID_EXACT);
1068 }
1069 
1070 /*
1071  *  SquishMsgnToUid; Returns the corresponding UID of a message number.
1072  */
1073 
SquishMsgnToUid(unsigned long n)1074 unsigned long SquishMsgnToUid(unsigned long n)
1075 {
1076     return MsgMsgnToUid(Ahandle, n);
1077 }
1078 
1079 /*
1080  *  SquishAreaSetLast; Sets the last message read in the .sql file and
1081  *  closes the message area.  If the .sql file doesn't exist, create it.
1082  */
1083 
SquishAreaSetLast(AREA * a)1084 int SquishAreaSetLast(AREA * a)
1085 {
1086     char work[255];
1087     long i = 1;
1088     int ret = TRUE;
1089     int fd;
1090     unsigned char buffer[4];
1091 
1092     if (mh != NULL)
1093     {
1094         MsgCloseMsg(mh);
1095         mh = NULL;
1096     }
1097 
1098     if (Ahandle != NULL)
1099     {
1100         sprintf(work, "%s.sql", a->path);
1101 
1102         fd = sopen(work, O_BINARY | O_RDWR, SH_DENYNO, S_IMODE);
1103         if (fd == -1)
1104         {
1105             if (errno != EACCES && errno != EMFILE)
1106             {
1107 	        fd = sopen(work, O_BINARY | O_WRONLY | O_CREAT, SH_DENYNO,
1108 			   S_IMODE);
1109                 if (fd == -1)
1110                 {
1111                     ret = FALSE;
1112                 }
1113                 else
1114                 {
1115                     buffer[0] = buffer[1] = buffer[2] = buffer[3] = '\0';
1116                     lseek(fd, 0L, SEEK_SET);
1117                     for (i = 0; SW->useroffset > (int)i; i++)
1118                     {
1119                         farwrite(fd, buffer, 4);
1120                     }
1121 
1122                     i = MsgMsgnToUid(Ahandle, CurArea.lastread);
1123                     buffer[0] = i & 0xFF;
1124                     buffer[1] = (i >> 8) & 0xFF;
1125                     buffer[2] = (i >> 16) & 0xFF;
1126                     buffer[3] = (i >> 24) & 0xFF;
1127 
1128                     farwrite(fd, buffer, 4);
1129                     close(fd);
1130                 }
1131             }
1132             else
1133             {
1134                 ret = FALSE;
1135             }
1136         }
1137         else
1138         {
1139             lseek(fd, SW->useroffset * 4, SEEK_SET);
1140 
1141             if (SW->use_lastr)
1142             {
1143                 i = MsgMsgnToUid(Ahandle, CurArea.lastread);
1144             }
1145             else
1146             {
1147                 i = MsgMsgnToUid(Ahandle, CurArea.current);
1148             }
1149 
1150             buffer[0] = i & 0xFF;
1151             buffer[1] = (i >> 8) & 0xFF;
1152             buffer[2] = (i >> 16) & 0xFF;
1153             buffer[3] = (i >> 24) & 0xFF;
1154 
1155             farwrite(fd, buffer, 4);
1156             close(fd);
1157         }
1158     }
1159     return ret;
1160 }
1161 
1162 /*
1163  *  SquishMsgDelete; Erases a message in the current area, specified
1164  *  by the passed index.
1165  */
1166 
SquishMsgDelete(unsigned long n)1167 int SquishMsgDelete(unsigned long n)
1168 {
1169     unsigned long msgn;
1170     msgn = MsgUidToMsgn(Ahandle, n, UID_EXACT);
1171     if (MsgKillMsg(Ahandle, msgn) == -1)
1172     {
1173         return FALSE;
1174     }
1175     return TRUE;
1176 }
1177 
stampToTimeT(struct _stamp * st)1178 static time_t stampToTimeT(struct _stamp *st)
1179 {
1180     time_t tt;
1181     struct tm tms;
1182     tms.tm_sec = st->time.ss << 1;
1183     tms.tm_min = st->time.mm;
1184     tms.tm_hour = st->time.hh;
1185     tms.tm_mday = st->date.da;
1186     tms.tm_mon = st->date.mo - 1;
1187     tms.tm_year = st->date.yr + 80;
1188     tms.tm_isdst = -1;
1189     tt = mktime(&tms);
1190     return tt;
1191 }
1192 
timeTToStamp(time_t tt)1193 static struct _stamp *timeTToStamp(time_t tt)
1194 {
1195     struct tm *tmsp;
1196     static struct _stamp st;
1197     tmsp = localtime(&tt);
1198     st.time.ss = tmsp->tm_sec >> 1;
1199     st.time.mm = tmsp->tm_min;
1200     st.time.hh = tmsp->tm_hour;
1201     st.date.da = tmsp->tm_mday;
1202     st.date.mo = tmsp->tm_mon + 1;
1203     st.date.yr = tmsp->tm_year - 80;
1204     return &st;
1205 }
1206 
MsgApiInit(void)1207 void MsgApiInit(void)
1208 {
1209     minf.def_zone = 0;          /* set default zone */
1210     minf.req_version = 0;       /* level 0 of the MsgAPI */
1211     MsgOpenApi(&minf);          /* init the MsgAPI  */
1212 }
1213 
MsgApiTerm(void)1214 void MsgApiTerm(void)
1215 {
1216     MsgCloseApi();
1217 }
1218