1 /*
2  *  SMAPI; Modified Squish MSGAPI
3  *
4  *  Squish MSGAPI0 is copyright 1991 by Scott J. Dudley.  All rights reserved.
5  *  Modifications released to the public domain.
6  *
7  *  Use of this file is subject to the restrictions contain in the Squish
8  *  MSGAPI0 licence agreement.  Please refer to licence.txt for complete
9  *  details of the licencing restrictions.  If you do not find the text
10  *  of this agreement in licence.txt, or if you do not have this file,
11  *  you should contact Scott Dudley at FidoNet node 1:249/106 or Internet
12  *  e-mail Scott.Dudley@f106.n249.z1.fidonet.org.
13  *
14  *  In no event should you proceed to use any of the source files in this
15  *  archive without having accepted the terms of the MSGAPI0 licensing
16  *  agreement, or such other agreement as you are able to reach with the
17  *  author.
18  */
19 
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 
29 #include <huskylib/compiler.h>
30 
31 #ifdef HAS_IO_H
32 #include <io.h>
33 #endif
34 
35 #ifdef HAS_SHARE_H
36 #include <share.h>
37 #endif
38 
39 #ifdef HAS_DOS_H
40 #include <dos.h>
41 #endif
42 
43 #ifdef HAS_MALLOC_H
44 #include <malloc.h>
45 #endif
46 
47 #ifdef __BEOS__
48 #include <be/kernel/fs_attr.h>
49 #include <be/support/TypeConstants.h>
50 #endif
51 
52 #define MSGAPI_HANDLERS
53 
54 #include <huskylib/huskylib.h>
55 
56 /* Swith for build DLL */
57 #define DLLEXPORT
58 #include <huskylib/huskyext.h>
59 
60 #include "msgapi.h"
61 #include "old_msg.h"
62 #include "api_sdm.h"
63 #include "api_sdmp.h"
64 #include "apidebug.h"
65 
66 #define SDM_BLOCK 256     /* Should be exp2(x) */
67 #define Mhd ((struct _sdmdata *)(mh->apidata))
68 #define MsghMhd ((struct _sdmdata *)(((struct _msgh *)msgh)->sq->apidata))
69 
70 static byte *hwm_from = (byte *) "-=|smapi internal|=-";
71 
SdmOpenArea(byte * name,word mode,word type)72 MSGA *MSGAPI SdmOpenArea(byte * name, word mode, word type)
73 {
74     MSGA *mh;
75 
76     if( !name || !*name )
77     {
78        msgapierr = MERR_BADNAME;
79        return NULL;
80     }
81 
82     mh = palloc(sizeof(MSGA));
83     if (mh == NULL)
84     {
85         msgapierr = MERR_NOMEM;
86         goto ErrOpen;
87     }
88 
89     memset(mh, '\0', sizeof(MSGA));
90 
91     mh->id = MSGAPI_ID;
92 
93     if (type & MSGTYPE_ECHO)
94     {
95         mh->isecho = TRUE;
96     }
97 
98     mh->api = (struct _apifuncs *)palloc(sizeof(struct _apifuncs));
99     if (mh->api == NULL)
100     {
101         msgapierr = MERR_NOMEM;
102         goto ErrOpen;
103     }
104 
105     memset(mh->api, '\0', sizeof(struct _apifuncs));
106 
107     mh->apidata = (void *)palloc(sizeof(struct _sdmdata));
108     if (mh->apidata == NULL)
109     {
110         msgapierr = MERR_NOMEM;
111         goto ErrOpen;
112     }
113 
114     memset((byte *) mh->apidata, '\0', sizeof(struct _sdmdata));
115 
116     strcpy((char *) Mhd->base, (char *) name);
117     Add_Trailing((char *) Mhd->base, PATH_DELIM);
118     Mhd->hwm = (dword) - 1L;
119 
120     mh->len = sizeof(MSGA);
121     mh->num_msg = 0;
122     mh->high_msg = 0;
123     mh->high_water = (dword) - 1L;
124 
125     if (!direxist((char *) name) && (mode == MSGAREA_NORMAL
126        || _createDirectoryTree((char *) name) != 0))
127     {
128         msgapierr = MERR_NOENT;
129         goto ErrOpen;
130     }
131 
132     if (!_SdmRescanArea(mh))
133     {
134         goto ErrOpen;
135     }
136 
137     mh->type &= ~MSGTYPE_ECHO;
138 
139     *mh->api = sdm_funcs;
140     mh->sz_xmsg = sizeof(XMSG);
141 
142     msgapierr = 0;
143     return mh;
144 
145 ErrOpen:
146 
147     if (mh)
148     {
149         if (mh->api)
150         {
151             if (mh->apidata)
152             {
153                 pfree(mh->apidata);
154             }
155 
156             pfree(mh->api);
157         }
158 
159         pfree(mh);
160     }
161 
162     return NULL;
163 }
164 
SdmDeleteBase(char * name)165 int SdmDeleteBase(char *name)
166 {
167     FFIND *ff;
168     char *temp;
169 
170     if(!name || !*name){ msgapierr=MERR_BADNAME; return FALSE; }
171 
172     temp = malloc(strlen(name)+6);
173     if (temp == NULL)
174       return FALSE;
175     sprintf(temp, "%s*.msg", name);
176 
177     ff = FFindOpen(temp, 0);
178 
179     free(temp);
180 
181     if (ff != 0)
182     {
183       do
184 	  {
185 	    temp = malloc(strlen(name) + strlen(ff->ff_name) + 1);
186 	    if (temp == NULL)
187 	    {
188 	        FFindClose(ff);
189 	        return FALSE;
190 	    }
191 	    sprintf(temp, "%s%s", name, ff->ff_name);
192 	    unlink(temp);
193 	    free(temp);
194 	  }
195       while (FFindNext(ff) == 0);
196 
197       FFindClose(ff);
198     }
199     sprintf(temp, "%slastread", name);
200     unlink(temp);
201     rmdir(name);
202 
203     return TRUE; /* rmdir error is ok */
204 }
205 
SdmCloseArea(MSGA * mh)206 static sword _XPENTRY SdmCloseArea(MSGA * mh)
207 {
208     XMSG msg;
209     MSGH *msgh;
210     static byte *msgbody = (byte *)
211     "NOECHO\r\rPlease ignore.  This message is only used by the SquishMail "
212     "system to store\rthe high water mark for each conference area.\r\r\r\r"
213     "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r(Elvis was here!)\r\r\r";
214 
215     if (InvalidMh(mh))
216     {
217         return -1;
218     }
219 
220     if (Mhd->hwm_chgd)
221     {
222         msgh = SdmOpenMsg(mh, MOPEN_CREATE, 1L);
223         if (msgh != NULL)
224         {
225             Init_Xmsg(&msg);
226 
227             Get_Dos_Date((union stamp_combo *)&msg.date_arrived);
228             Get_Dos_Date((union stamp_combo *)&msg.date_written);
229 
230             /*
231              *  Use high-bit chars in the to/from field, so that (l)users
232              *  can't log on as this userid and delete the HWM.
233              */
234 
235             strcpy((char *) msg.from, (char *) hwm_from);
236             strcpy((char *) msg.to, (char *) msg.from);
237             strcpy((char *) msg.subj, "High wadda' mark");
238 
239             /* To prevent "INTL 0:0/0 0:0/0" kludges */
240             msg.orig.zone = msg.dest.zone = mi.def_zone;
241 
242             msg.replyto = mh->high_water;
243             msg.attr = MSGPRIVATE | MSGREAD | MSGLOCAL | MSGSENT;
244 
245             SdmWriteMsg(msgh, FALSE, &msg, msgbody, strlen((char *) msgbody) + 1,
246               strlen((char *) msgbody) + 1, 0L, NULL);
247 
248             SdmCloseMsg(msgh);
249         }
250     }
251 
252     if (Mhd->msgs_open)
253     {
254         msgapierr = MERR_EOPEN;
255         return -1;
256     }
257 
258     if (Mhd->msgnum)
259     {
260         pfree(Mhd->msgnum);
261     }
262 
263     pfree(mh->apidata);
264     pfree(mh->api);
265 
266     mh->id = 0L;
267     pfree(mh);
268 
269     msgapierr = MERR_NONE;
270     return 0;
271 }
272 
SdmOpenMsg(MSGA * mh,word mode,dword msgnum)273 static MSGH *_XPENTRY SdmOpenMsg(MSGA * mh, word mode, dword msgnum)
274 {
275     byte msgname[PATHLEN];
276     int handle, filemode;
277     int owrite = FALSE;
278     dword msguid=0;
279 
280     MSGH *msgh;
281 
282     if (InvalidMh(mh))
283     {
284         return NULL;
285     }
286 
287     if (msgnum == MSGNUM_CUR)
288     {
289         msgnum = mh->cur_msg;
290     }
291     else if (msgnum == MSGNUM_PREV)
292     {
293 	msgnum = mh->cur_msg - 1;
294 	if (msgnum == 0)
295 	{
296 	    msgapierr = MERR_NOENT;
297 	    return NULL;
298 	}
299     }
300     else if (msgnum == MSGNUM_NEXT)
301     {
302 	msgnum = mh->num_msg + 1;
303 	if (msgnum > mh->num_msg)
304 	{
305 	    msgapierr = MERR_NOENT;
306 	    return NULL;
307 	}
308     }
309     else if (mode != MOPEN_CREATE)
310     {
311         /*
312          *  If we're not creating, make sure that the specified msg# can
313          *  be found.
314          */
315 	if (msgnum <= 0 || msgnum > mh->num_msg)
316 	{
317 	    msgapierr = MERR_NOENT;
318 	    return NULL;
319 	}
320     }
321     if (msgnum <= mh->num_msg && msgnum > 0)
322 	msguid = SdmMsgnToUid(mh, msgnum);
323 
324     if (mode == MOPEN_CREATE)
325     {
326         /* If we're creating a new message... */
327 
328         if (msgnum == 0L)
329         {
330             /*
331              *  If the base isn't locked, make sure that we avoid
332              *  conflicts...
333              */
334 
335             if (!mh->locked)
336             {
337                 /* Check to see if the msg we're writing already exists */
338 
339                 sprintf((char *) msgname, (char *) sd_msg, Mhd->base, (sdm_msgnum_type)mh->high_msg + 1);
340 
341                 if (fexist((char *) msgname))
342                 {
343                     /* If so, rescan the base, to find out which msg# it is. */
344 
345                     if (Mhd->msgnum && Mhd->msgnum_len)
346                     {
347                         pfree(Mhd->msgnum);
348                     }
349 
350                     if (!_SdmRescanArea(mh))
351                     {
352                         return NULL;
353                     }
354                 }
355             }
356 
357             msgnum = ++mh->num_msg;
358 	    msguid = ++mh->high_msg;
359 
360             /*
361              *  Make sure that we don't overwrite the high-water mark,
362              *  unless we call with msgnum != 0L (a specific number).
363              */
364 
365             if (mh->isecho && msgnum == 1)
366             {
367                 msgnum = mh->high_msg = 2;
368             }
369         }
370         else
371         {
372             /* otherwise, we're overwriting an existing message */
373             owrite = TRUE;
374         }
375 
376         filemode = O_CREAT | O_TRUNC | O_RDWR;
377     }
378     else if (mode == MOPEN_READ)
379     {
380         filemode = O_RDONLY;
381     }
382     else if (mode == MOPEN_WRITE)
383     {
384         filemode = O_WRONLY;
385     }
386     else
387     {
388         filemode = O_RDWR;
389     }
390 
391     sprintf((char *) msgname, (char *) sd_msg, Mhd->base, (int)msguid);
392     handle = sopen((char *) msgname, filemode | O_BINARY, SH_DENYNONE,
393                    FILEMODE(mh->isecho));
394     if (handle == -1)
395     {
396         if (filemode & O_CREAT)
397         {
398             msgapierr = MERR_BADF;
399         }
400         else
401         {
402             msgapierr = MERR_NOENT;
403         }
404         return NULL;
405     }
406 
407     mh->cur_msg = msgnum;
408 
409     msgh = palloc(sizeof(MSGH));
410     if (msgh == NULL)
411     {
412         close(handle);
413         msgapierr = MERR_NOMEM;
414         return NULL;
415     }
416 
417     memset(msgh, '\0', sizeof(MSGH));
418     msgh->fd = handle;
419 
420     if (mode == MOPEN_CREATE)
421     {
422         if (mh->num_msg == (dword)(1 << (sizeof(mh->num_msg) * 8 - 1)))
423         {   /* Messagebase implementaion (size) limit (messages counter is full)*/
424             pfree(msgh);
425             close(handle);
426             msgapierr = MERR_LIMIT;
427             return NULL;
428         }
429         if ((mh->num_msg + 1) >= Mhd->msgnum_len)
430         {
431             word msgnum_len_new = Mhd->msgnum_len + (word) SDM_BLOCK;
432             Mhd->msgnum = realloc(Mhd->msgnum, msgnum_len_new * sizeof(unsigned));
433 
434             if (!Mhd->msgnum)
435             {
436                 pfree(msgh);
437                 close(handle);
438                 msgapierr = MERR_NOMEM;
439 		return NULL;
440             }
441             Mhd->msgnum_len = msgnum_len_new;
442         }
443 
444         /*
445          *  If we're writing a new msg, this is easy -- just add to
446          *  end of list.
447          */
448 
449         if (!owrite)
450         {
451 	    Mhd->msgnum[(size_t) (mh->num_msg)] = (word) msguid;
452 	    mh->num_msg++;
453         }
454         else
455         {
456             /*
457              *  If this message is already in the list then do nothing --
458              *  simply overwrite it, keeping the same message number, so
459              *  no action is required.  Otherwise, we have to shift
460              *  everything up by one since we're adding this new message
461              *  in between two others.
462              */
463 
464             if ((dword) Mhd->msgnum[msgnum-1] != msguid)
465             {
466                 memmove(Mhd->msgnum + msgnum, Mhd->msgnum + msgnum - 1,
467                   ((size_t) mh->num_msg - msgnum) * sizeof(Mhd->msgnum[0]));
468                 Mhd->msgnum[msgnum-1] = (word) msguid;
469                 mh->num_msg++;
470             }
471         }
472     }
473 
474     msgh->cur_pos = 0L;
475 
476     if (mode == MOPEN_CREATE)
477     {
478         msgh->msg_len = 0;
479     }
480     else
481     {
482         msgh->msg_len = (dword) - 1;
483     }
484 
485     msgh->sq = mh;
486     msgh->id = MSGH_ID;
487     msgh->ctrl = NULL;
488     msgh->clen = -1;
489     msgh->zplen = 0;
490 
491     msgapierr = MERR_NONE;
492 
493     /* Keep track of how many messages were opened for this area */
494 
495     MsghMhd->msgs_open++;
496 
497     return msgh;
498 }
499 
SdmCloseMsg(MSGH * msgh)500 static sword _XPENTRY SdmCloseMsg(MSGH * msgh)
501 {
502     if (InvalidMsgh(msgh))
503     {
504         return -1;
505     }
506 
507     MsghMhd->msgs_open--;
508 
509     if (msgh->ctrl)
510     {
511         pfree(msgh->ctrl);
512         msgh->ctrl = NULL;
513     }
514 
515     close(msgh->fd);
516 
517     msgh->id = 0L;
518     pfree(msgh);
519 
520     msgapierr = MERR_NONE;
521     return 0;
522 }
523 
SdmReadMsg(MSGH * msgh,XMSG * msg,dword offset,dword bytes,byte * text,dword clen,byte * ctxt)524 static dword _XPENTRY SdmReadMsg(MSGH * msgh, XMSG * msg, dword offset, dword bytes, byte * text, dword clen, byte * ctxt)
525 {
526     unsigned len;
527     dword realbytes, got;
528     struct _omsg fmsg;
529     word need_ctrl;
530     byte *fake_msgbuf = NULL, *newtext;
531 
532     if (InvalidMsgh(msgh))
533     {
534         return (dword)-1L;
535     }
536 
537     if (!(clen && ctxt))
538     {
539         clen = 0L;
540         ctxt = NULL;
541     }
542 
543     if (!(text && bytes))
544     {
545         bytes = 0L;
546         text = NULL;
547     }
548 
549     if (msg)
550     {
551         lseek(msgh->fd, 0L, SEEK_SET);
552 
553         if (!read_omsg(msgh->fd, &fmsg))
554         {
555             msgapierr = MERR_BADF;
556             return (dword)-1L;
557         }
558 
559         fmsg.to[sizeof(fmsg.to) - 1] = '\0';
560         fmsg.from[sizeof(fmsg.from) - 1] = '\0';
561         fmsg.subj[sizeof(fmsg.subj) - 1] = '\0';
562         fmsg.date[sizeof(fmsg.date) - 1] = '\0';
563 
564         Convert_Fmsg_To_Xmsg(&fmsg, msg, mi.def_zone);
565 
566         StripNasties(msg->from);
567         StripNasties(msg->to);
568         StripNasties(msg->subj);
569     }
570 
571 
572     /*
573      *  If we weren't instructed to read some message text (ie. only the
574      *  header, read a block anyway.  We need to scan for kludge lines,
575      *  to pick out the appropriate zone/point info.)
576      */
577 
578     if (msgh->ctrl == NULL && ((msg || ctxt || text) ||
579       (msg || ctxt || text) == 0))
580     {
581         need_ctrl = TRUE;
582     }
583     else
584     {
585         need_ctrl = FALSE;
586     }
587 
588     realbytes = bytes;
589     unused(realbytes);
590 
591     /*
592      *  If we need to read the control information, and the user hasn't
593      *  requested a read operation, we'll need to do one anyway.
594      */
595 
596     if (need_ctrl && text == NULL)
597     {
598 	struct stat st;
599 	fstat(msgh->fd, &st);
600         text = fake_msgbuf = palloc(st.st_size - OMSG_SIZE + 1);
601         if (text == NULL)
602         {
603             msgapierr = MERR_NOMEM;
604             return (dword)-1L;
605         }
606 
607         text[st.st_size - OMSG_SIZE] = '\0';
608         bytes = st.st_size - OMSG_SIZE;
609     }
610 
611     /* If we need to read in some text... */
612 
613     if (text)
614     {
615         /* Seek is superfluous if we just read msg header */
616 
617         if (!msg || msgh->msgtxt_start != 0)
618         {
619             lseek(msgh->fd, (dword) OMSG_SIZE +
620               msgh->msgtxt_start + offset, SEEK_SET);
621 
622             msgh->cur_pos = offset;
623         }
624 
625         got = (dword) farread(msgh->fd, text, (unsigned int)bytes);
626         text[(unsigned int)got] = '\0';
627 
628         /*
629          *  Update counter only if we got some text, and only if we're
630          *  doing a read requested by the user (as opposed to reading
631          *  ahead to find kludge lines).
632          */
633 
634         if (got > 0 && !fake_msgbuf)
635         {
636             msgh->cur_pos += got;
637         }
638     }
639     else
640     {
641         got = 0;
642     }
643 
644     /* Convert the kludges into 'ctxt' format */
645 
646     if (need_ctrl && got && offset == 0L)
647     {
648         len = got;
649 
650         msgh->ctrl = CopyToControlBuf(text, &newtext, &len);
651         if (msgh->ctrl != NULL)
652         {
653             msgh->clen = (dword) strlen((char *) msgh->ctrl) + 1;
654             msgh->msgtxt_start = newtext - text;
655 
656             /* Shift back the text buffer to counter absence of ^a strings */
657 
658             memmove(text, newtext, (size_t) (bytes - (newtext - text)));
659 
660             got -= (dword) (msgh->clen - 1);
661         }
662     }
663 
664     /* Scan the ctxt ourselves to find zone/point info */
665 
666     if (msg && msgh->ctrl)
667     {
668         ConvertControlInfo(msgh->ctrl, &msg->orig, &msg->dest);
669     }
670 
671     /* And if the app requested ctrlinfo, put it in its place. */
672 
673     if (ctxt && msgh->ctrl)
674     {
675         size_t slen;
676 
677         slen = strlen((char *) msgh->ctrl) + 1;
678         memmove(ctxt, msgh->ctrl, min(slen, (size_t) clen));
679         ctxt[min(slen, (size_t) clen)] = '\0';
680     }
681 
682     if (fake_msgbuf)
683     {
684         pfree(fake_msgbuf);
685         got = 0;
686     }
687 
688     msgapierr = MERR_NONE;
689     return got;
690 }
691 
SdmWriteMsg(MSGH * msgh,word append,XMSG * msg,byte * text,dword textlen,dword totlen,dword clen,byte * ctxt)692 static sword _XPENTRY SdmWriteMsg(MSGH * msgh, word append, XMSG * msg, byte * text, dword textlen, dword totlen, dword clen, byte * ctxt)
693 {
694     struct _omsg fmsg;
695     byte *s;
696 
697     unused(totlen);
698 
699     if (clen == 0L || ctxt == NULL)
700     {
701         ctxt = NULL;
702         clen = 0L;
703     }
704 
705     if (InvalidMsgh(msgh))
706     {
707         return -1;
708     }
709 
710     lseek(msgh->fd, 0L, SEEK_SET);
711 
712     if (msg)
713     {
714         Convert_Xmsg_To_Fmsg(msg, &fmsg);
715 
716         if (!write_omsg(msgh->fd, &fmsg))
717         {
718             msgapierr = MERR_NODS;
719             return -1;
720         }
721 
722         if (!append && msgh->clen <= 0 && msgh->zplen == 0 && !msgh->sq->isecho)
723         {
724             statfd = msgh->fd;
725             msgh->zplen = (word) WriteZPInfo(msg, WriteToFd, ctxt);
726         }
727 
728         /* Use Attributes und BeOS */
729 #ifdef __BEOS__
730         {
731         struct tm tmdate;
732         time_t ttime;
733 
734         fs_write_attr(msgh->fd, "BEOS:TYPE", B_MIME_TYPE, 0l, "message/fmsg", 13);
735         fs_write_attr(msgh->fd, "XMSG:FROM", B_STRING_TYPE, 0l, msg->from, strlen((char*)msg->from));
736         fs_write_attr(msgh->fd, "XMSG:TO"  , B_STRING_TYPE, 0l, msg->to, strlen((char*)msg->to));
737         fs_write_attr(msgh->fd, "XMSG:SUBJ", B_STRING_TYPE, 0l, msg->subj, strlen((char*)msg->subj));
738         ttime = mktime(DosDate_to_TmDate((union stamp_combo *)&msg->date_written, &tmdate));
739         fs_write_attr(msgh->fd, "XMSG:DATE", B_TIME_TYPE, 0l, &ttime, 4l);
740         ttime = mktime(DosDate_to_TmDate((union stamp_combo *)&msg->date_arrived, &tmdate));
741         fs_write_attr(msgh->fd, "XMSG:DTAR", B_TIME_TYPE, 0l, &ttime, 4l);
742         /* ... and so on ... not fully implemented ! (Yet) */
743         }
744 #endif
745     }
746     else if (!append || ctxt)
747     {
748         /* Skip over old message header */
749         lseek(msgh->fd, (dword) OMSG_SIZE +
750           (dword) msgh->zplen, SEEK_SET);
751     }
752 
753     /* Now write the control info / kludges */
754 
755     if (clen && ctxt)
756     {
757         if (!msg)
758         {
759             lseek(msgh->fd, (dword) OMSG_SIZE +
760               (dword) msgh->zplen, SEEK_SET);
761         }
762 
763         s = CvtCtrlToKludge(ctxt);
764 
765         if (s)
766         {
767             unsigned sl_s = (unsigned) strlen((char *)s);
768             int ret;
769 
770             ret = farwrite(msgh->fd, s, sl_s);
771             pfree(s);
772             if (ret != (int) sl_s)
773             {
774                 msgapierr = MERR_NODS;
775                 return -1;
776             }
777         }
778     }
779 
780     if (append)
781     {
782         lseek(msgh->fd, 0L, SEEK_END);
783     }
784 
785     if (text)
786     {
787         if (farwrite(msgh->fd, text, (unsigned)textlen) != (signed) textlen)
788         {
789             msgapierr = MERR_NODS;
790             return -1;
791         }
792         if (text[textlen])
793             if (farwrite(msgh->fd, "", 1) != 1)
794             {
795                 msgapierr = MERR_NODS;
796                 return -1;
797             }
798     }
799 
800     msgapierr = MERR_NONE;
801     return 0;
802 }
803 
SdmKillMsg(MSGA * mh,dword msgnum)804 static sword _XPENTRY SdmKillMsg(MSGA * mh, dword msgnum)
805 {
806     dword hwm;
807     byte temp[PATHLEN];
808     UMSGID msguid;
809 
810     if (InvalidMh(mh))
811     {
812         return -1;
813     }
814 
815     if (msgnum > mh->num_msg || msgnum <= 0)
816     {
817 	msgapierr = MERR_NOENT;
818 	return -1;
819     }
820 
821     msguid = SdmMsgnToUid(mh, msgnum);
822 
823     /* Remove the message number from our private index */
824 
825     memmove(Mhd->msgnum + msgnum - 1, Mhd->msgnum + msgnum,
826             (int)(mh->num_msg - msgnum) * sizeof(Mhd->msgnum[0]));
827 
828     /* If we couldn't find it, return an error message */
829 
830     sprintf((char *) temp, (char *) sd_msg, Mhd->base, (unsigned int)msguid);
831 
832     if (unlink((char *) temp) == -1)
833     {
834         msgapierr = MERR_NOENT;
835         return -1;
836     }
837 
838     mh->num_msg--;
839 
840     /* Adjust the high message number */
841 
842     if (msguid == mh->high_msg)
843     {
844         if (mh->num_msg)
845         {
846             mh->high_msg = SdmMsgnToUid(mh, mh->num_msg);
847         }
848         else
849         {
850             mh->high_msg = 0;
851         }
852     }
853 
854     /* Now adjust the high-water mark, if necessary */
855 
856     hwm = SdmGetHighWater(mh);
857 
858     if (hwm != (dword) -1 && hwm > 0 && hwm >= msgnum)
859     {
860         SdmSetHighWater(mh, msgnum - 1);
861     }
862 
863     if (mh->cur_msg >= msgnum)
864 	mh->cur_msg--;
865 
866     msgapierr = MERR_NONE;
867     return 0;
868 }
869 
SdmLock(MSGA * mh)870 static sword _XPENTRY SdmLock(MSGA * mh)
871 {
872     if (InvalidMh(mh))
873     {
874         return -1;
875     }
876 
877     msgapierr = MERR_NONE;
878     return 0;
879 }
880 
SdmUnlock(MSGA * mh)881 static sword _XPENTRY SdmUnlock(MSGA * mh)
882 {
883     if (InvalidMh(mh))
884     {
885         return -1;
886     }
887 
888     msgapierr = MERR_NONE;
889     return 0;
890 }
891 
SdmValidate(byte * name)892 sword MSGAPI SdmValidate(byte * name)
893 {
894     msgapierr = MERR_NONE;
895     return ((sword) (direxist((char *) name) != FALSE));
896 }
897 
SdmSetCurPos(MSGH * msgh,dword pos)898 static sword _XPENTRY SdmSetCurPos(MSGH * msgh, dword pos)
899 {
900     if (InvalidMsgh(msgh))
901     {
902         return 0;
903     }
904 
905     lseek(msgh->fd, msgh->cur_pos = pos, SEEK_SET);
906     msgapierr = MERR_NONE;
907     return 0;
908 }
909 
SdmGetCurPos(MSGH * msgh)910 static dword _XPENTRY SdmGetCurPos(MSGH * msgh)
911 {
912     if (InvalidMsgh(msgh))
913     {
914         return (dword)-1L;
915     }
916 
917     msgapierr = MERR_NONE;
918     return msgh->cur_pos;
919 }
920 
SdmMsgnToUid(MSGA * mh,dword msgnum)921 static UMSGID _XPENTRY SdmMsgnToUid(MSGA * mh, dword msgnum)
922 {
923     if (InvalidMh(mh))
924     {
925         return (UMSGID) - 1;
926     }
927 
928     msgapierr = MERR_NONE;
929     if (msgnum > mh->num_msg) return (UMSGID)-1;
930     if (msgnum <= 0) return 0;
931     return (UMSGID) Mhd->msgnum[msgnum - 1];
932 }
933 
SdmUidToMsgn(MSGA * mh,UMSGID umsgid,word type)934 static dword _XPENTRY SdmUidToMsgn(MSGA * mh, UMSGID umsgid, word type)
935 {
936     dword  left, right, nnew;
937     UMSGID umsg;
938 
939     if (InvalidMh(mh))
940         return (dword)-1L;
941     if (umsgid <= 0)
942 	return 0;
943     left = 1;
944     right = mh->num_msg;
945     while (left <= right)
946     {
947 	nnew = (right + left) / 2;
948 	umsg = SdmMsgnToUid(mh, nnew);
949 	if (umsg == (UMSGID)-1)
950 	    return 0;
951 	if (umsg < umsgid)
952 	    left = nnew + 1;
953 	else if (umsg > umsgid){
954 	    if(nnew>0) right = nnew - 1;
955 	    else right=0;
956 	}else
957 	    return nnew;
958     }
959     if (type == UID_EXACT) return 0;
960     if (type == UID_PREV)
961 	return right;
962     return (left > mh->num_msg) ? mh->num_msg : left;
963 }
964 
SdmGetHighWater(MSGA * mh)965 static dword _XPENTRY SdmGetHighWater(MSGA * mh)
966 {
967     MSGH *msgh;
968     XMSG msg;
969 
970     if (InvalidMh(mh))
971     {
972         return (dword)-1L;
973     }
974 
975     /* If we've already fetched the highwater mark... */
976 
977     if (mh->high_water != (dword) - 1L)
978     {
979         return SdmUidToMsgn(mh, mh->high_water, UID_PREV);
980     }
981 
982     msgh = SdmOpenMsg(mh, MOPEN_READ, 1L);
983     if (msgh == NULL)
984     {
985         return 0L;
986     }
987 
988     if (SdmReadMsg(msgh, &msg, 0L, 0L, NULL, 0L, NULL) == (dword) - 1 ||
989       (strcmp((char *) msg.from, (char *) hwm_from)!=0) )
990     {
991         mh->high_water = 0L;
992     }
993     else
994     {
995         mh->high_water = (dword) msg.replyto;
996     }
997 
998     SdmCloseMsg(msgh);
999 
1000     return SdmUidToMsgn(mh, mh->high_water, UID_PREV);
1001 }
1002 
SdmSetHighWater(MSGA * mh,dword hwm)1003 static sword _XPENTRY SdmSetHighWater(MSGA * mh, dword hwm)
1004 {
1005     if (InvalidMh(mh))
1006     {
1007         return -1;
1008     }
1009 
1010     /*
1011      *  Only write it to memory for now.  We'll do a complete update of
1012      *  the real HWM in 1.MSGA only when doing a MsgCloseArea(), to save
1013      *  time.
1014      */
1015 
1016     if (hwm != mh->high_water)
1017     {
1018         Mhd->hwm_chgd = TRUE;
1019     }
1020 
1021     mh->high_water = hwm;
1022     return 0;
1023 }
1024 
SdmGetTextLen(MSGH * msgh)1025 static dword _XPENTRY SdmGetTextLen(MSGH * msgh)
1026 {
1027     dword pos, end;
1028 
1029     if(InvalidMsgh(msgh))
1030     {
1031         return (dword)-1L;
1032     }
1033 
1034     /* Figure out the physical length of the message */
1035 
1036     if (msgh->msg_len == -1)
1037     {
1038         pos = (dword) tell(msgh->fd);
1039         end = lseek(msgh->fd, 0L, SEEK_END);
1040 
1041         if (end < OMSG_SIZE)
1042         {
1043             msgh->msg_len = 0L;
1044         }
1045         else
1046         {
1047             msgh->msg_len = end - (dword) OMSG_SIZE;
1048         }
1049 
1050         lseek(msgh->fd, pos, SEEK_SET);
1051     }
1052 
1053     /* If we've already figured out the length of the control info */
1054 
1055     if ((msgh->clen == -1) && (_Grab_Clen(msgh) == -1))
1056     {
1057         return 0;
1058     }
1059     else
1060     {
1061         return (dword) (msgh->msg_len - msgh->msgtxt_start);
1062     }
1063 }
1064 
SdmGetCtrlLen(MSGH * msgh)1065 static dword _XPENTRY SdmGetCtrlLen(MSGH * msgh)
1066 {
1067 
1068     if(InvalidMsgh(msgh))
1069     {
1070         return (dword)-1L;
1071     }
1072 
1073     /* If we've already figured out the length of the control info */
1074 
1075     if ((msgh->clen == -1) && (_Grab_Clen(msgh) == -1))
1076     {
1077         return 0;
1078     }
1079     else
1080     {
1081         return (dword) msgh->clen;
1082     }
1083 }
1084 
_Grab_Clen(MSGH * msgh)1085 static sword near _Grab_Clen(MSGH * msgh)
1086 {
1087     if ((sdword) SdmReadMsg(msgh, NULL, 0L, 0L, NULL, 0L, NULL) < (sdword) 0)
1088     {
1089         return (sword) -1;
1090     }
1091     else
1092     {
1093         return (sword) 0;
1094     }
1095 }
1096 
_SdmRescanArea(MSGA * mh)1097 static sword near _SdmRescanArea(MSGA * mh)
1098 {
1099     FFIND *ff;
1100     char *temp;
1101     word mn, thismsg;
1102 
1103     if(InvalidMh(mh))
1104     {
1105         return -1;
1106     }
1107 
1108     mh->num_msg = 0;
1109 
1110     Mhd->msgnum = palloc(SDM_BLOCK * sizeof(unsigned));
1111     if (Mhd->msgnum == NULL)
1112     {
1113         msgapierr = MERR_NOMEM;
1114         return FALSE;
1115     }
1116 
1117     Mhd->msgnum_len = SDM_BLOCK;
1118 
1119     temp = malloc(strlen((char *)Mhd->base)+6);
1120     if (temp == NULL)
1121       return -1;
1122     sprintf((char *) temp, "%s*.msg", Mhd->base);
1123 
1124     ff = FFindOpen((char *) temp, 0);
1125 
1126     free(temp);
1127 
1128     if (ff != 0)
1129     {
1130         mn = 0;
1131 
1132         do
1133         {
1134             /* Don't count zero-length or invalid messages */
1135 
1136 #ifndef __UNIX__
1137             if (ff->ff_fsize < OMSG_SIZE)
1138             {
1139                  continue;
1140             }
1141 #endif
1142 
1143             if (mh->num_msg == (dword)(1 << (sizeof(mh->num_msg) * 8 - 1)))
1144             {   /* Messagebase implementaion (size) limit (messages counter is full)*/
1145                 msgapierr = MERR_LIMIT;
1146                 return FALSE;
1147             }
1148             if (mn >= Mhd->msgnum_len)
1149             {
1150                 word msgnum_len_new = Mhd->msgnum_len + (word) SDM_BLOCK;
1151                 Mhd->msgnum = realloc(Mhd->msgnum, msgnum_len_new * sizeof(unsigned));
1152 
1153                 if (!Mhd->msgnum)
1154                 {
1155                     msgapierr = MERR_NOMEM;
1156                     return FALSE;
1157                 }
1158                 Mhd->msgnum_len = msgnum_len_new;
1159             }
1160 
1161             thismsg = (word) atoi(ff->ff_name);
1162             if (thismsg != 0)
1163             {
1164                 Mhd->msgnum[mn++] = thismsg;
1165 
1166                 if ((dword) thismsg > mh->high_msg)
1167                 {
1168                     mh->high_msg = (dword) thismsg;
1169                 }
1170 
1171                 mh->num_msg = (dword) mn;
1172             }
1173 
1174 #ifdef __OS2__
1175             if ((mn % 128) == 127)
1176                 tdelay(1L); /* give up cpu */
1177 #endif
1178         }
1179         while (FFindNext(ff) == 0);
1180 
1181         FFindClose(ff);
1182 
1183         /* Now sort the list of messages */
1184         qksort((int *)Mhd->msgnum, (word) mh->num_msg);
1185     }
1186 
1187     return TRUE;
1188 }
1189 
Init_Xmsg(XMSG * msg)1190 static void MSGAPI Init_Xmsg(XMSG * msg)
1191 {
1192     if(InvalidMsg(msg))
1193     {
1194         return;
1195     }
1196     memset(msg, '\0', sizeof(XMSG));
1197 }
1198 
Convert_Fmsg_To_Xmsg(struct _omsg * fmsg,XMSG * msg,word def_zone)1199 static void MSGAPI Convert_Fmsg_To_Xmsg(struct _omsg *fmsg, XMSG * msg,
1200   word def_zone)
1201 {
1202     NETADDR *orig, *dest;
1203 
1204     if(!fmsg){
1205         msgapierr = MERR_BADH;
1206         return;
1207     }
1208     if(InvalidMsg(msg))
1209     {
1210         return;
1211     }
1212 
1213     Init_Xmsg(msg);
1214 
1215     orig = &msg->orig;
1216     dest = &msg->dest;
1217 
1218     fmsg->to[sizeof(fmsg->to) - 1] = '\0';
1219     fmsg->from[sizeof(fmsg->from) - 1] = '\0';
1220     fmsg->subj[sizeof(fmsg->subj) - 1] = '\0';
1221     fmsg->date[sizeof(fmsg->date) - 1] = '\0';
1222 
1223     strcpy((char *) msg->from, (char *) fmsg->from);
1224     strcpy((char *) msg->to, (char *) fmsg->to);
1225     strcpy((char *) msg->subj, (char *) fmsg->subj);
1226 
1227     orig->zone = dest->zone = def_zone;
1228     orig->point = dest->point = 0;
1229 
1230     orig->net = fmsg->orig_net;
1231     orig->node = fmsg->orig;
1232 
1233     dest->net = fmsg->dest_net;
1234     dest->node = fmsg->dest;
1235 
1236     Get_Binary_Date(&msg->date_written, &fmsg->date_written, fmsg->date);
1237     Get_Binary_Date(&msg->date_arrived, &fmsg->date_arrived, fmsg->date);
1238 
1239     strcpy((char *) msg->__ftsc_date, (char *) fmsg->date);
1240 
1241     msg->utc_ofs = 0;
1242 
1243     msg->replyto = fmsg->reply;
1244     msg->replies[0] = fmsg->up;
1245     msg->attr = (dword) fmsg->attr;
1246     msg->xmtimesread = fmsg->times;
1247     msg->xmcost = fmsg->cost;
1248 }
1249 
Convert_Xmsg_To_Fmsg(XMSG * msg,struct _omsg * fmsg)1250 static void MSGAPI Convert_Xmsg_To_Fmsg(XMSG * msg, struct _omsg *fmsg)
1251 {
1252     NETADDR *orig, *dest;
1253 
1254     if(!fmsg){
1255         msgapierr = MERR_BADH;
1256         return;
1257     }
1258     if(InvalidMsg(msg))
1259     {
1260         return;
1261     }
1262     memset(fmsg, '\0', sizeof(struct _omsg));
1263 
1264     orig = &msg->orig;
1265     dest = &msg->dest;
1266 
1267     strncpy((char *) fmsg->from, (char *) msg->from, sizeof(fmsg->from));
1268     strncpy((char *) fmsg->to, (char *) msg->to, sizeof(fmsg->to));
1269     strncpy((char *) fmsg->subj, (char *) msg->subj, sizeof(fmsg->subj));
1270 
1271     fmsg->from[sizeof(fmsg->from) - 1] = '\0';
1272     fmsg->to[sizeof(fmsg->to) - 1] = '\0';
1273     fmsg->subj[sizeof(fmsg->subj) - 1] = '\0';
1274 
1275     fmsg->orig_net = orig->net;
1276     fmsg->orig = orig->node;
1277 
1278     fmsg->dest_net = dest->net;
1279     fmsg->dest = dest->node;
1280 
1281     if (*msg->__ftsc_date)
1282     {
1283         strncpy((char *) fmsg->date, (char *) msg->__ftsc_date, sizeof(fmsg->date));
1284         fmsg->date[sizeof(fmsg->date) - 1] = '\0';
1285     }
1286     else
1287     {
1288         sprintf((char *) fmsg->date, "%02d %s %02d  %02d:%02d:%02d",
1289           msg->date_written.date.da ? msg->date_written.date.da : 1,
1290           months_ab[msg->date_written.date.mo ? msg->date_written.date.mo - 1 : 0],
1291           (msg->date_written.date.yr + 80) % 100, msg->date_written.time.hh,
1292           msg->date_written.time.mm, msg->date_written.time.ss << 1);
1293     }
1294 
1295     fmsg->date_written = msg->date_written;
1296     fmsg->date_arrived = msg->date_arrived;
1297 
1298     fmsg->reply = (word) msg->replyto;
1299     fmsg->up = (word) msg->replies[0];
1300     fmsg->attr = (word) (msg->attr & 0xffffL);
1301     fmsg->times = (word)msg->xmtimesread;
1302     fmsg->cost =  (word)msg->xmcost;
1303 }
1304 
WriteZPInfo(XMSG * msg,void (_stdc * wfunc)(byte * str),byte * kludges)1305 int _XPENTRY WriteZPInfo(XMSG * msg, void (_stdc * wfunc) (byte * str), byte * kludges)
1306 {
1307     byte temp[PATHLEN], *null = (byte *) "";
1308     int bytes = 0;
1309 
1310     if(!wfunc){
1311         msgapierr = MERR_BADH;
1312         return -1;
1313     }
1314     if(InvalidMsg(msg))
1315     {
1316         return -1;
1317     }
1318 
1319     if (!kludges)
1320     {
1321         kludges = null;
1322     }
1323 
1324     if ((msg->dest.zone != mi.def_zone || msg->orig.zone != mi.def_zone) &&
1325       !strstr((char *) kludges, "\001INTL"))
1326     {
1327         sprintf((char *) temp, "\001INTL %hu:%hu/%hu %hu:%hu/%hu\r", msg->dest.zone, msg->dest.net,
1328           msg->dest.node, msg->orig.zone, msg->orig.net, msg->orig.node);
1329 
1330         (*wfunc) (temp);
1331         bytes += strlen((char *) temp);
1332     }
1333 
1334     if (msg->orig.point && !strstr((char *) kludges, "\001" "FMPT"))
1335     {
1336         sprintf((char *) temp, "\001" "FMPT %hu\r", msg->orig.point);
1337         (*wfunc) (temp);
1338         bytes += strlen((char *) temp);
1339     }
1340 
1341     if (msg->dest.point && !strstr((char *) kludges, "\001" "TOPT"))
1342     {
1343         sprintf((char *) temp, "\001" "TOPT %hu\r", msg->dest.point);
1344         (*wfunc) (temp);
1345         bytes += strlen((char *) temp);
1346     }
1347 
1348     return bytes;
1349 }
1350 
WriteToFd(byte * str)1351 static void _stdc WriteToFd(byte * str)
1352 {
1353   if(str && *str)
1354   {
1355     if( 0 > farwrite(statfd, str, strlen((char *) str)) )
1356       msgapierr = MERR_BADF;
1357   }
1358   else msgapierr = MERR_BADH;
1359 }
1360 
Get_Binary_Date(struct _stamp * todate,struct _stamp * fromdate,byte * asciidate)1361 static void near Get_Binary_Date(struct _stamp *todate, struct _stamp *fromdate, byte * asciidate)
1362 {
1363   if(!todate || !fromdate || !asciidate){
1364     msgapierr = MERR_BADH;
1365     return;
1366   }
1367 
1368     if (fromdate->date.da == 0 || fromdate->date.da > 31 || fromdate->date.yr > 50 ||
1369       fromdate->time.hh > 23 || fromdate->time.mm > 59 || fromdate->time.ss > 59 ||
1370       ((union stamp_combo *)(void *)&fromdate)->ldate == 0)
1371     {
1372         ASCII_Date_To_Binary((char *) asciidate, (union stamp_combo *)todate);
1373     }
1374     else
1375     {
1376         *todate = *fromdate;
1377     }
1378 }
1379 
SdmGetHash(HAREA mh,dword msgnum)1380 static dword _XPENTRY SdmGetHash(HAREA mh, dword msgnum)
1381 {
1382   XMSG xmsg;
1383   HMSG msgh;
1384   dword rc = 0l;
1385 
1386   msgh=SdmOpenMsg(mh, MOPEN_READ, msgnum);
1387   if (msgh==NULL)
1388     return (dword) 0l;
1389 
1390   if (SdmReadMsg(msgh, &xmsg, 0L, 0L, NULL, 0L, NULL)!=(dword)-1)
1391   {
1392     rc = SquishHash(xmsg.to) | (xmsg.attr & MSGREAD) ? 0x80000000l : 0;
1393   }
1394 
1395   SdmCloseMsg(msgh);
1396 
1397   msgapierr=MERR_NONE;
1398   return rc;
1399 }
1400 
SdmGetNextUid(HAREA ha)1401 static UMSGID _XPENTRY SdmGetNextUid(HAREA ha)
1402 {
1403   if (InvalidMh(ha))
1404     return 0L;
1405 
1406   if (!ha->locked)
1407   {
1408     msgapierr=MERR_NOLOCK;
1409     return 0L;
1410   }
1411 
1412   msgapierr=MERR_NONE;
1413   return ha->high_msg+1;
1414 }
1415