1 /* smblib.c */
2 
3 /* Synchronet message base (SMB) library routines */
4 
5 /* $Id: smblib.cpp,v 1.7 2011/02/17 11:18:16 stas_degteff Exp $ */
6 
7 /****************************************************************************
8  * @format.tab-size 4       (Plain Text/Source Code File Header)            *
9  * @format.use-tabs true    (see http://www.synchro.net/ptsc_hdr.html)      *
10  *                                                                          *
11  * Copyright 2000 Rob Swindell - http://www.synchro.net/copyright.html      *
12  *                                                                          *
13  * This library is free software; you can redistribute it and/or            *
14  * modify it under the terms of the GNU Lesser General Public License       *
15  * as published by the Free Software Foundation; either version 2           *
16  * of the License, or (at your option) any later version.                   *
17  * See the GNU Lesser General Public License for more details: lgpl.txt or  *
18  * http://www.fsf.org/copyleft/lesser.html                                  *
19  *                                                                          *
20  * Anonymous FTP access to the most recent released source is available at  *
21  * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net  *
22  *                                                                          *
23  * Anonymous CVS access to the development source and modification history  *
24  * is available at cvs.synchro.net:/cvsroot/sbbs, example:                  *
25  * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login            *
26  *     (just hit return, no password is necessary)                          *
27  * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src     *
28  *                                                                          *
29  * For Synchronet coding style and modification guidelines, see             *
30  * http://www.synchro.net/source.html                                       *
31  *                                                                          *
32  * You are encouraged to submit any modifications (preferably in Unix diff  *
33  * format) via e-mail to mods@synchro.net                                   *
34  *                                                                          *
35  * Note: If this box doesn't appear square, then you need to fix your tabs. *
36  ****************************************************************************/
37 
38 #if defined __WATCOMC__ || defined __TURBOC__
39     #include <mem.h>
40 #else
41     #include <memory.h>
42 #endif
43 
44 #ifdef __WATCOMC__
45     #include <dos.h>
46 #elif defined __TURBOC__
47     #include <dir.h>
48 #endif
49 
50 /* ANSI C Library headers */
51 
52 #ifdef HAVE_MALLOC_H
53     #include <malloc.h>
54 #endif
55 
56 #include <time.h>
57 #include <errno.h>
58 #include <fcntl.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <sys/types.h>
63 #include <sys/stat.h>
64 
65 /* SMB-specific headers */
66 #include "smblib.h"
67 #include <gtimall.h>
68 #include <gfilutil.h>
69 
70 /* Use smb_ver() and smb_lib_ver() to obtain these values */
71 #define SMBLIB_VERSION      "2.16"      /* SMB library version */
72 #define SMB_VERSION         0x0121      /* SMB format version */
73                                         /* High byte major, low byte minor */
74 
75 #define U_MODE 777 /* permitions for the new files (real: U_MODE XOR UMASK) */
76                    /* This is required for sopen(,,,) */
77 
78 #ifndef __gtimall_h
gtime(time32_t * timep)79 time32_t gtime(time32_t *timep)
80 {
81   time32_t temp = (time32_t)time(NULL);
82   return timep ? *timep = temp : temp;
83 }
84 #endif  //#ifndef __gtimall_h
85 
smb_ver(void)86 int SMBCALL smb_ver(void)
87 {
88     return(SMB_VERSION);
89 }
90 
smb_lib_ver(void)91 char* SMBCALL smb_lib_ver(void)
92 {
93     return(SMBLIB_VERSION);
94 }
95 
96 /****************************************************************************/
97 /* Open a message base of name 'smb->file'                                  */
98 /* Opens files for READing messages or updating message indices only        */
99 /****************************************************************************/
smb_open(smb_t * smb)100 int SMBCALL smb_open(smb_t* smb)
101 {
102     int file;
103     char str[128];
104     smbhdr_t hdr;
105 
106     /* Set default values, if uninitialized */
107     if(!smb->retry_time)
108         smb->retry_time=10;     /* seconds */
109     if(!smb->retry_delay
110         || smb->retry_delay>(smb->retry_time*100))  /* at least ten retries */
111         smb->retry_delay=250;   /* milliseconds */
112     smb->shd_fp=smb->sdt_fp=smb->sid_fp=NULL;
113     smb->last_error[0]=0;
114     sprintf(str,"%s.shd",smb->file);
115     if((file=sopen(str,O_RDWR|O_CREAT|O_BINARY,SH_DENYNO,U_MODE))==-1) {
116         sprintf(smb->last_error,"%d opening %s",errno,str);
117         return(2);
118     }
119 
120     if((smb->shd_fp=fdopen(file,"r+b"))==NULL) {
121         sprintf(smb->last_error,"%d fdopening %s (%d)",errno,str,file);
122         close(file);
123         return(4);
124     }
125 
126     if(filelength(file)>=sizeof(smbhdr_t)) {
127         setvbuf(smb->shd_fp,smb->shd_buf,_IONBF,SHD_BLOCK_LEN);
128         if(smb_locksmbhdr(smb)!=0) {
129             smb_close(smb);
130             /* smb_lockmsghdr set last_error */
131             return(-1);
132         }
133         memset(&hdr,0,sizeof(smbhdr_t));
134         if(fread(&hdr,sizeof(smbhdr_t),1,smb->shd_fp)!=1) {
135             sprintf(smb->last_error,"reading header");
136             smb_close(smb);
137             return(-10);
138         }
139         if(memcmp(hdr.id,SMB_HEADER_ID,LEN_HEADER_ID)) {
140             sprintf(smb->last_error,"corrupt SMB header ID: %.*s",LEN_HEADER_ID,hdr.id);
141             smb_close(smb);
142             return(-2);
143         }
144         if(hdr.version<0x110) {         /* Compatibility check */
145             sprintf(smb->last_error,"insufficient header version: %X",hdr.version);
146             smb_close(smb);
147             return(-3);
148         }
149         if(fread(&(smb->status),1,sizeof(smbstatus_t),smb->shd_fp)
150             !=sizeof(smbstatus_t)) {
151             sprintf(smb->last_error,"failed to read status");
152             smb_close(smb);
153             return(-4);
154         }
155         smb_unlocksmbhdr(smb);
156         rewind(smb->shd_fp);
157     }
158 
159     setvbuf(smb->shd_fp,smb->shd_buf,_IOFBF,SHD_BLOCK_LEN);
160 
161     sprintf(str,"%s.sdt",smb->file);
162     if((file=sopen(str,O_RDWR|O_CREAT|O_BINARY,SH_DENYNO,U_MODE))==-1) {
163         sprintf(smb->last_error,"%d opening %s",errno,str);
164         smb_close(smb);
165         return(1);
166     }
167 
168     if((smb->sdt_fp=fdopen(file,"r+b"))==NULL) {
169         sprintf(smb->last_error,"%d fdopening %s (%d)",errno,str,file);
170         close(file);
171         smb_close(smb);
172         return(5);
173     }
174 
175     setvbuf(smb->sdt_fp,NULL,_IOFBF,2*1024);
176 
177     sprintf(str,"%s.sid",smb->file);
178     if((file=sopen(str,O_RDWR|O_CREAT|O_BINARY,SH_DENYNO,U_MODE))==-1) {
179         sprintf(smb->last_error,"%d opening %s",errno,str);
180         smb_close(smb);
181         return(3);
182     }
183 
184     if((smb->sid_fp=fdopen(file,"r+b"))==NULL) {
185         sprintf(smb->last_error,"%d fdopening %s (%d)",errno,str,file);
186         close(file);
187         smb_close(smb);
188         return(6);
189     }
190 
191     setvbuf(smb->sid_fp,NULL,_IOFBF,2*1024);
192 
193     return(0);
194 }
195 
196 /****************************************************************************/
197 /* Closes the currently open message base                                   */
198 /****************************************************************************/
smb_close(smb_t * smb)199 void SMBCALL smb_close(smb_t* smb)
200 {
201     if(smb->shd_fp!=NULL) {
202         smb_unlocksmbhdr(smb);         /* In case it's been locked */
203         fclose(smb->shd_fp);
204     }
205     if(smb->sid_fp!=NULL)
206         fclose(smb->sid_fp);
207     if(smb->sdt_fp!=NULL)
208         fclose(smb->sdt_fp);
209     smb->sid_fp=smb->shd_fp=smb->sdt_fp=NULL;
210 }
211 
212 /****************************************************************************/
213 /* Opens the data block allocation table message base 'smb->file'           */
214 /* Retrys for retry_time number of seconds                                  */
215 /* Return 0 on success, non-zero otherwise                                  */
216 /****************************************************************************/
smb_open_da(smb_t * smb)217 int SMBCALL smb_open_da(smb_t* smb)
218 {
219     int     file;
220     char    str[128];
221     time32_t start = 0;
222 
223     sprintf(str,"%s.sda",smb->file);
224     while(1) {
225         if((file=sopen(str,O_RDWR|O_CREAT|O_BINARY,SH_DENYRW,U_MODE))!=-1)
226             break;
227         if(errno!=EACCES && errno!=EAGAIN) {
228             sprintf(smb->last_error,"%d opening %s",errno,str);
229             return(-1);
230         }
231         if(!start)
232             start = gtime(NULL);
233         else
234             if(gtime(NULL)-start >= smb->retry_time) {
235                 sprintf(smb->last_error,"timeout opening %s (retry_time=%d)"
236                     ,str,smb->retry_time);
237                 return(-2);
238             }
239         usleep(smb->retry_delay);
240     }
241     if((smb->sda_fp=fdopen(file,"r+b"))==NULL) {
242         sprintf(smb->last_error,"%d fdopening %s (%d)",errno,str,file);
243         close(file);
244         return(-3);
245     }
246     setvbuf(smb->sda_fp,NULL,_IOFBF,2*1024);
247     return(0);
248 }
249 
smb_close_da(smb_t * smb)250 void SMBCALL smb_close_da(smb_t* smb)
251 {
252     if(smb->sda_fp!=NULL)
253         fclose(smb->sda_fp);
254     smb->sda_fp=NULL;
255 }
256 
257 /****************************************************************************/
258 /* Opens the header block allocation table for message base 'smb.file'      */
259 /* Retrys for smb.retry_time number of seconds                              */
260 /* Return 0 on success, non-zero otherwise                                  */
261 /****************************************************************************/
smb_open_ha(smb_t * smb)262 int SMBCALL smb_open_ha(smb_t* smb)
263 {
264     int     file;
265     char    str[128];
266     time32_t start = 0;
267 
268     sprintf(str,"%s.sha",smb->file);
269     while(1) {
270         if((file=sopen(str,O_RDWR|O_CREAT|O_BINARY,SH_DENYRW,U_MODE))!=-1)
271             break;
272         if(errno!=EACCES && errno!=EAGAIN) {
273             sprintf(smb->last_error,"%d opening %s",errno,str);
274             return(-1);
275         }
276         if(!start)
277             start = gtime(NULL);
278         else
279             if(gtime(NULL)-start >= smb->retry_time) {
280                 sprintf(smb->last_error,"timeout opening %s (retry_time=%d)"
281                     ,str,smb->retry_time);
282                 return(-2);
283             }
284         usleep(smb->retry_delay);
285     }
286     if((smb->sha_fp=fdopen(file,"r+b"))==NULL) {
287         sprintf(smb->last_error,"%d fdopening %s (%d)",errno,str,file);
288         close(file);
289         return(-3);
290     }
291     setvbuf(smb->sha_fp,NULL,_IOFBF,2*1024);
292     return(0);
293 }
294 
smb_close_ha(smb_t * smb)295 void SMBCALL smb_close_ha(smb_t* smb)
296 {
297     if(smb->sha_fp!=NULL)
298         fclose(smb->sha_fp);
299     smb->sha_fp=NULL;
300 }
301 
302 /****************************************************************************/
303 /* If the parameter 'push' is non-zero, this function stores the currently  */
304 /* open message base to the "virtual" smb stack. Up to SMB_STACK_LEN        */
305 /* message bases may be stored (defined in SMBDEFS.H).                      */
306 /* The parameter 'op' is the operation to perform on the stack. Either      */
307 /* SMB_STACK_PUSH, SMB_STACK_POP, or SMB_STACK_XCHNG                        */
308 /* If the operation is SMB_STACK_POP, this function restores a message base */
309 /* previously saved with a SMB_STACK_PUSH call to this same function.       */
310 /* If the operation is SMB_STACK_XCHNG, then the current message base is    */
311 /* exchanged with the message base on the top of the stack (most recently   */
312 /* pushed.                                                                  */
313 /* If the current message base is not open, the SMB_STACK_PUSH and          */
314 /* SMB_STACK_XCHNG operations do nothing                                    */
315 /* Returns 0 on success, non-zero if stack full.                            */
316 /* If operation is SMB_STACK_POP or SMB_STACK_XCHNG, it always returns 0.   */
317 /****************************************************************************/
smb_stack(smb_t * smb,int op)318 int SMBCALL smb_stack(smb_t* smb, int op)
319 {
320     static smb_t stack[SMB_STACK_LEN];
321     static int  stack_idx;
322     smb_t       tmp_smb;
323 
324     if(op==SMB_STACK_PUSH) {
325         if(stack_idx>=SMB_STACK_LEN) {
326             sprintf(smb->last_error,"SMB stack overflow");
327             return(1);
328         }
329         if(smb->shd_fp==NULL || smb->sdt_fp==NULL || smb->sid_fp==NULL)
330             return(0);    /* Msg base not open */
331         memcpy(&stack[stack_idx],smb,sizeof(smb_t));
332         stack_idx++;
333         return(0);
334     }
335     /* pop or xchng */
336     if(!stack_idx)  /* Nothing on the stack, so do nothing */
337         return(0);
338     if(op==SMB_STACK_XCHNG) {
339         if(smb->shd_fp==NULL)
340             return(0);
341         memcpy(&tmp_smb,smb,sizeof(smb_t));
342     }
343 
344     stack_idx--;
345     memcpy(smb,&stack[stack_idx],sizeof(smb_t));
346     if(op==SMB_STACK_XCHNG) {
347         memcpy(&stack[stack_idx],&tmp_smb,sizeof(smb_t));
348         stack_idx++;
349     }
350     return(0);
351 }
352 
353 /****************************************************************************/
354 /* Truncates header file                                                    */
355 /* Retrys for smb.retry_time number of seconds                              */
356 /* Return 0 on success, non-zero otherwise                                  */
357 /****************************************************************************/
smb_trunchdr(smb_t * smb)358 int SMBCALL smb_trunchdr(smb_t* smb)
359 {
360     time32_t start = 0;
361 
362     if(smb->shd_fp==NULL) {
363         sprintf(smb->last_error,"msgbase not open");
364         return(SMB_ERR_NOT_OPEN);
365     }
366     rewind(smb->shd_fp);
367     while(1) {
368         if(!chsize(fileno(smb->shd_fp),0L))
369             break;
370         if(errno!=EACCES && errno!=EAGAIN) {
371             sprintf(smb->last_error,"%d changing header file size",errno);
372             return(-1);
373         }
374         if(!start)
375             start = gtime(NULL);
376         else
377             if(gtime(NULL)-start >= smb->retry_time) {  /* Time-out */
378                 sprintf(smb->last_error,"timeout changing header file size (retry_time=%d)"
379                     ,smb->retry_time);
380                 return(-2);
381             }
382         usleep(smb->retry_delay);
383     }
384     return(0);
385 }
386 
387 /*********************************/
388 /* Message Base Header Functions */
389 /*********************************/
390 
391 /****************************************************************************/
392 /* Attempts for smb.retry_time number of seconds to lock the msg base hdr   */
393 /****************************************************************************/
smb_locksmbhdr(smb_t * smb)394 int SMBCALL smb_locksmbhdr(smb_t* smb)
395 {
396     time32_t start = 0;
397 
398     if(smb->shd_fp==NULL) {
399         sprintf(smb->last_error,"msgbase not open");
400         return(SMB_ERR_NOT_OPEN);
401     }
402     while(1) {
403         if(lock(fileno(smb->shd_fp),0L,sizeof(smbhdr_t)+sizeof(smbstatus_t))==0) {
404             smb->locked=1;  /* TRUE */
405             return(0);
406         }
407         if(!start)
408             start = gtime(NULL);
409         else
410             if(gtime(NULL)-start >= smb->retry_time)
411                 break;
412         /* In case we've already locked it */
413         if(unlock(fileno(smb->shd_fp),0L,sizeof(smbhdr_t)+sizeof(smbstatus_t))==0)
414             smb->locked=0;  /* FALSE */
415         usleep(smb->retry_delay);
416     }
417     sprintf(smb->last_error,"timeout locking header");
418     return(-1);
419 }
420 
421 /****************************************************************************/
422 /* Read the SMB header from the header file and place into smb.status       */
423 /****************************************************************************/
smb_getstatus(smb_t * smb)424 int SMBCALL smb_getstatus(smb_t* smb)
425 {
426     int     i;
427 
428     if(smb->shd_fp==NULL) {
429         sprintf(smb->last_error,"msgbase not open");
430         return(SMB_ERR_NOT_OPEN);
431     }
432     setvbuf(smb->shd_fp,smb->shd_buf,_IONBF,SHD_BLOCK_LEN);
433     clearerr(smb->shd_fp);
434     fseek(smb->shd_fp,sizeof(smbhdr_t),SEEK_SET);
435     i=fread(&(smb->status),1,sizeof(smbstatus_t),smb->shd_fp);
436     setvbuf(smb->shd_fp,smb->shd_buf,_IOFBF,SHD_BLOCK_LEN);
437     if(i==sizeof(smbstatus_t))
438         return(0);
439     sprintf(smb->last_error,"read %d instead of %d",i,(int)sizeof(smbstatus_t));
440     return(1);
441 }
442 
443 /****************************************************************************/
444 /* Writes message base header                                               */
445 /****************************************************************************/
smb_putstatus(smb_t * smb)446 int SMBCALL smb_putstatus(smb_t* smb)
447 {
448     int i;
449 
450     if(smb->shd_fp==NULL) {
451         sprintf(smb->last_error,"msgbase not open");
452         return(SMB_ERR_NOT_OPEN);
453     }
454     clearerr(smb->shd_fp);
455     fseek(smb->shd_fp,sizeof(smbhdr_t),SEEK_SET);
456     i=fwrite(&(smb->status),1,sizeof(smbstatus_t),smb->shd_fp);
457     fflush(smb->shd_fp);
458     if(i==sizeof(smbstatus_t))
459         return(0);
460     sprintf(smb->last_error,"wrote %d instead of %d",i,(int)sizeof(smbstatus_t));
461     return(1);
462 }
463 
464 /****************************************************************************/
465 /* Unlocks previously locks message base header                             */
466 /****************************************************************************/
smb_unlocksmbhdr(smb_t * smb)467 int SMBCALL smb_unlocksmbhdr(smb_t* smb)
468 {
469     int result;
470 
471     if(smb->shd_fp==NULL) {
472         sprintf(smb->last_error,"msgbase not open");
473         return(SMB_ERR_NOT_OPEN);
474     }
475     result = unlock(fileno(smb->shd_fp),0L,sizeof(smbhdr_t)+sizeof(smbstatus_t));
476     if(result==0)
477         smb->locked=0;  /* FALSE */
478     return(result);
479 }
480 
481 /********************************/
482 /* Individual Message Functions */
483 /********************************/
484 
485 /****************************************************************************/
486 /* Attempts for smb.retry_time number of seconds to lock the hdr for 'msg'  */
487 /****************************************************************************/
smb_lockmsghdr(smb_t * smb,smbmsg_t * msg)488 int SMBCALL smb_lockmsghdr(smb_t* smb, smbmsg_t* msg)
489 {
490     time32_t start = 0;
491 
492     if(smb->shd_fp==NULL) {
493         sprintf(smb->last_error,"msgbase not open");
494         return(SMB_ERR_NOT_OPEN);
495     }
496     while(1) {
497         if(!lock(fileno(smb->shd_fp),msg->idx.offset,sizeof(msghdr_t)))
498             return(0);
499         if(!start)
500             start = gtime(NULL);
501         else
502             if(gtime(NULL)-start >= smb->retry_time)
503                 break;
504         /* In case we've already locked it */
505         unlock(fileno(smb->shd_fp),msg->idx.offset,sizeof(msghdr_t));
506         usleep(smb->retry_delay);
507     }
508     sprintf(smb->last_error,"timeout locking header");
509     return(-1);
510 }
511 
512 /****************************************************************************/
513 /* Fills msg->idx with message index based on msg->hdr.number               */
514 /* OR if msg->hdr.number is 0, based on msg->offset (record offset).        */
515 /* if msg.hdr.number does not equal 0, then msg->offset is filled too.      */
516 /* Either msg->hdr.number or msg->offset must be initialized before         */
517 /* calling this function                                                    */
518 /* Returns 1 if message number wasn't found, 0 if it was                    */
519 /****************************************************************************/
smb_getmsgidx(smb_t * smb,smbmsg_t * msg)520 int SMBCALL smb_getmsgidx(smb_t* smb, smbmsg_t* msg)
521 {
522     idxrec_t idx;
523     uint32_t     l,length,total,bot,top;
524 
525     if(smb->sid_fp==NULL) {
526         sprintf(smb->last_error,"index not open");
527         return(SMB_ERR_NOT_OPEN);
528     }
529     clearerr(smb->sid_fp);
530     if(!msg->hdr.number) {
531         fseek(smb->sid_fp,msg->offset*sizeof(idxrec_t),SEEK_SET);
532         if(!fread(&msg->idx,sizeof(idxrec_t),1,smb->sid_fp)) {
533             sprintf(smb->last_error,"reading index");
534             return(1);
535         }
536         return(0);
537     }
538 
539     length=filelength(fileno(smb->sid_fp));
540     if(!length) {
541         sprintf(smb->last_error,"invalid index file length: %d",length);
542         return(1);
543     }
544     total=length/sizeof(idxrec_t);
545     if(!total) {
546         sprintf(smb->last_error,"invalid index file length: %d",length);
547         return(1);
548     }
549 
550     bot=0;
551     top=total;
552     l=total/2; /* Start at middle index */
553     while(1) {
554         fseek(smb->sid_fp,l*sizeof(idxrec_t),SEEK_SET);
555         if(!fread(&idx,sizeof(idxrec_t),1,smb->sid_fp)) {
556             sprintf(smb->last_error,"reading index");
557             return(1);
558         }
559         if(bot==top-1 && idx.number!=msg->hdr.number) {
560             sprintf(smb->last_error,"msg %d not found",msg->hdr.number);
561             return(1);
562         }
563         if(idx.number>msg->hdr.number) {
564             top=l;
565             l=bot+((top-bot)/2);
566             continue;
567         }
568         if(idx.number<msg->hdr.number) {
569             bot=l;
570             l=top-((top-bot)/2);
571             continue;
572         }
573         break;
574     }
575     msg->idx=idx;
576     msg->offset=l;
577     return(0);
578 }
579 
580 /****************************************************************************/
581 /* Reads the first index record in the open message base                    */
582 /****************************************************************************/
smb_getfirstidx(smb_t * smb,idxrec_t * idx)583 int SMBCALL smb_getfirstidx(smb_t* smb, idxrec_t *idx)
584 {
585     if(smb->sid_fp==NULL) {
586         sprintf(smb->last_error,"index not open");
587         return(SMB_ERR_NOT_OPEN);
588     }
589     clearerr(smb->sid_fp);
590     fseek(smb->sid_fp,0,SEEK_SET);
591     if(!fread(idx,sizeof(idxrec_t),1,smb->sid_fp)) {
592         sprintf(smb->last_error,"reading index");
593         return(-2);
594     }
595     return(0);
596 }
597 
598 /****************************************************************************/
599 /* Reads the last index record in the open message base                     */
600 /****************************************************************************/
smb_getlastidx(smb_t * smb,idxrec_t * idx)601 int SMBCALL smb_getlastidx(smb_t* smb, idxrec_t *idx)
602 {
603     int32_t length;
604 
605     if(smb->sid_fp==NULL) {
606         sprintf(smb->last_error,"index not open");
607         return(SMB_ERR_NOT_OPEN);
608     }
609     clearerr(smb->sid_fp);
610     length=filelength(fileno(smb->sid_fp));
611     if(length<sizeof(idxrec_t)) {
612         sprintf(smb->last_error,"invalid index file length: %d",length);
613         return(-1);
614     }
615     fseek(smb->sid_fp,length-sizeof(idxrec_t),SEEK_SET);
616     if(!fread(idx,sizeof(idxrec_t),1,smb->sid_fp)) {
617         sprintf(smb->last_error,"reading index");
618         return(-2);
619     }
620     return(0);
621 }
622 
623 /****************************************************************************/
624 /* Figures out the total length of the header record for 'msg'              */
625 /* Returns length                                                           */
626 /****************************************************************************/
smb_getmsghdrlen(smbmsg_t * msg)627 uint SMBCALL smb_getmsghdrlen(smbmsg_t* msg)
628 {
629     int i;
630 
631     /* fixed portion */
632     msg->hdr.length=sizeof(msghdr_t);
633     /* data fields */
634     msg->hdr.length+=msg->hdr.total_dfields*sizeof(dfield_t);
635     /* header fields */
636     for(i=0;i<msg->total_hfields;i++) {
637         msg->hdr.length+=sizeof(hfield_t);
638         msg->hdr.length+=msg->hfield[i].length;
639     }
640     return(msg->hdr.length);
641 }
642 
643 /****************************************************************************/
644 /* Figures out the total length of the data buffer for 'msg'                */
645 /* Returns length                                                           */
646 /****************************************************************************/
smb_getmsgdatlen(smbmsg_t * msg)647 uint32_t SMBCALL smb_getmsgdatlen(smbmsg_t* msg)
648 {
649     int i;
650     uint32_t length=0L;
651 
652     for(i=0;i<msg->hdr.total_dfields;i++)
653         length+=msg->dfield[i].length;
654     return(length);
655 }
656 
657 /****************************************************************************/
658 /* Read header information into 'msg' structure                             */
659 /* msg->idx.offset must be set before calling this function                 */
660 /* Must call smb_freemsgmem() to free memory allocated for var len strs     */
661 /* Returns 0 on success, non-zero if error                                  */
662 /****************************************************************************/
smb_getmsghdr(smb_t * smb,smbmsg_t * msg)663 int SMBCALL smb_getmsghdr(smb_t* smb, smbmsg_t* msg)
664 {
665     hfield_t  *vp;
666     void      **vpp;
667     uint16_t  i;
668     uint32_t  l, offset;
669     idxrec_t  idx;
670 
671     if(smb->shd_fp==NULL) {
672         sprintf(smb->last_error,"msgbase not open");
673         return(SMB_ERR_NOT_OPEN);
674     }
675     rewind(smb->shd_fp);
676     fseek(smb->shd_fp,msg->idx.offset,SEEK_SET);
677     idx=msg->idx;
678     offset=msg->offset;
679     memset(msg,0,sizeof(smbmsg_t));
680     msg->idx=idx;
681     msg->offset=offset;
682     if(!fread(&msg->hdr,sizeof(msghdr_t),1,smb->shd_fp)) {
683         sprintf(smb->last_error,"reading msg header");
684         return(-1);
685     }
686     if(memcmp(msg->hdr.id,SHD_HEADER_ID,LEN_HEADER_ID)) {
687         sprintf(smb->last_error,"corrupt message header ID: %.*s",LEN_HEADER_ID,msg->hdr.id);
688         return(-2);
689     }
690     if(msg->hdr.version<0x110) {
691         sprintf(smb->last_error,"insufficient header version: %X",msg->hdr.version);
692         return(-9);
693     }
694     l=sizeof(msghdr_t);
695     if(msg->hdr.total_dfields && (msg->dfield
696         =(dfield_t *)MALLOC(sizeof(dfield_t)*msg->hdr.total_dfields))==NULL) {
697         smb_freemsgmem(msg);
698         sprintf(smb->last_error,"malloc failure of %d bytes for %d data fields"
699             ,(int)sizeof(dfield_t)*msg->hdr.total_dfields, msg->hdr.total_dfields);
700         return(-3);
701     }
702     i=0;
703     while(i<msg->hdr.total_dfields && l<msg->hdr.length) {
704         if(!fread(&msg->dfield[i],sizeof(dfield_t),1,smb->shd_fp)) {
705             smb_freemsgmem(msg);
706             sprintf(smb->last_error,"reading data field %d",i);
707             return(-4);
708         }
709         i++;
710         l+=sizeof(dfield_t);
711     }
712     if(i<msg->hdr.total_dfields) {
713         smb_freemsgmem(msg);
714         sprintf(smb->last_error,"insufficient data fields read (%d instead of %d)"
715             ,i,msg->hdr.total_dfields);
716         return(-8);
717     }
718     while(l<msg->hdr.length) {
719         i=msg->total_hfields;
720         if((vpp=(void* *)REALLOC(msg->hfield_dat,sizeof(void* )*(i+1)))==NULL) {
721             smb_freemsgmem(msg);
722             sprintf(smb->last_error
723                 ,"realloc failure of %d bytes for header field data"
724                 ,(int)sizeof(void*)*(i+1));
725             return(-3);
726         }
727         msg->hfield_dat=vpp;
728         if((vp=(hfield_t *)REALLOC(msg->hfield,sizeof(hfield_t)*(i+1)))==NULL) {
729             smb_freemsgmem(msg);
730             sprintf(smb->last_error
731                 ,"realloc failure of %d bytes for header fields"
732                 ,(int)sizeof(hfield_t)*(i+1));
733             return(-3);
734         }
735         msg->hfield=vp;
736         msg->total_hfields++;
737         if(!fread(&msg->hfield[i],sizeof(hfield_t),1,smb->shd_fp)) {
738             smb_freemsgmem(msg);
739             sprintf(smb->last_error,"reading header field");
740             return(-5);
741         }
742         l+=sizeof(hfield_t);
743         if((msg->hfield_dat[i]=(char*)MALLOC(msg->hfield[i].length+1))
744             ==NULL) {           /* Allocate 1 extra for NULL terminator */
745             sprintf(smb->last_error
746                 ,"malloc failure of %d bytes for header field %d"
747                 ,msg->hfield[i].length+1, i);
748             smb_freemsgmem(msg);  /* or 0 length field */
749             return(-3);
750         }
751         memset(msg->hfield_dat[i],0,msg->hfield[i].length+1);  /* init to NULL */
752         if(msg->hfield[i].length
753             && !fread(msg->hfield_dat[i],msg->hfield[i].length,1,smb->shd_fp)) {
754             smb_freemsgmem(msg);
755             sprintf(smb->last_error,"reading header field data");
756             return(-6);
757         }
758         switch(msg->hfield[i].type) {   /* convenience variables */
759             case SENDER:
760                 if(!msg->from) {
761                     msg->from=(char *)msg->hfield_dat[i];
762                     break;
763                 }
764             case FORWARDED:     /* fall through */
765                 msg->forwarded=1;
766                 break;
767             case SENDERAGENT:
768                 if(!msg->forwarded)
769                     msg->from_agent=*(uint16_t *)msg->hfield_dat[i];
770                 break;
771             case SENDEREXT:
772                 if(!msg->forwarded)
773                     msg->from_ext=(char *)msg->hfield_dat[i];
774                 break;
775             case SENDERNETTYPE:
776                 if(!msg->forwarded)
777                     msg->from_net.type=*(uint16_t *)msg->hfield_dat[i];
778                 break;
779             case SENDERNETADDR:
780                 if(!msg->forwarded)
781                     msg->from_net.addr=(char *)msg->hfield_dat[i];
782                 break;
783             case REPLYTO:
784                 msg->replyto=(char *)msg->hfield_dat[i];
785                 break;
786             case REPLYTOEXT:
787                 msg->replyto_ext=(char *)msg->hfield_dat[i];
788                 break;
789             case REPLYTOAGENT:
790                 msg->replyto_agent=*(uint16_t *)msg->hfield_dat[i];
791                 break;
792             case REPLYTONETTYPE:
793                 msg->replyto_net.type=*(uint16_t *)msg->hfield_dat[i];
794                 break;
795             case REPLYTONETADDR:
796                 msg->replyto_net.addr=(char *)msg->hfield_dat[i];
797                 break;
798             case RECIPIENT:
799                 msg->to=(char *)msg->hfield_dat[i];
800                 break;
801             case RECIPIENTEXT:
802                 msg->to_ext=(char *)msg->hfield_dat[i];
803                 break;
804             case RECIPIENTAGENT:
805                 msg->to_agent=*(uint16_t *)msg->hfield_dat[i];
806                 break;
807             case RECIPIENTNETTYPE:
808                 msg->to_net.type=*(uint16_t *)msg->hfield_dat[i];
809                 break;
810             case RECIPIENTNETADDR:
811                 msg->to_net.addr=(char *)msg->hfield_dat[i];
812                 break;
813             case SUBJECT:
814                 msg->subj=(char *)msg->hfield_dat[i];
815                 break;
816             case RFC822MSGID:
817                 msg->id=(char *)msg->hfield_dat[i];
818                 break;
819             case RFC822REPLYID:
820                 msg->reply_id=(char *)msg->hfield_dat[i];
821                 break;
822             case USENETPATH:
823                 msg->path=(char *)msg->hfield_dat[i];
824                 break;
825             case USENETNEWSGROUPS:
826                 msg->newsgroups=(char *)msg->hfield_dat[i];
827                 break;
828             case FIDOMSGID:
829                 msg->ftn_msgid=(char *)msg->hfield_dat[i];
830                 break;
831             case FIDOREPLYID:
832                 msg->ftn_reply=(char *)msg->hfield_dat[i];
833                 break;
834             case FIDOAREA:
835                 msg->ftn_area=(char *)msg->hfield_dat[i];
836                 break;
837             case FIDOPID:
838                 msg->ftn_pid=(char *)msg->hfield_dat[i];
839                 break;
840             case FIDOFLAGS:
841                 msg->ftn_flags=(char *)msg->hfield_dat[i];
842                 break;
843 
844         }
845         l+=msg->hfield[i].length;
846     }
847 
848     if(!msg->from || !msg->to || !msg->subj) {
849         sprintf(smb->last_error,"missing required header field (from/to/subj)");
850         smb_freemsgmem(msg);
851         return(-7);
852     }
853     return(0);
854 }
855 
856 /****************************************************************************/
857 /* Frees memory allocated for 'msg'                                         */
858 /****************************************************************************/
smb_freemsgmem(smbmsg_t * msg)859 void SMBCALL smb_freemsgmem(smbmsg_t* msg)
860 {
861     uint16_t  i;
862 
863     if(msg->dfield) {
864         FREE(msg->dfield);
865         msg->dfield=NULL;
866     }
867     for(i=0;i<msg->total_hfields;i++)
868         if(msg->hfield_dat[i]) {
869             FREE(msg->hfield_dat[i]);
870             msg->hfield_dat[i]=NULL;
871         }
872     msg->total_hfields=0;
873     if(msg->hfield) {
874         FREE(msg->hfield);
875         msg->hfield=NULL;
876     }
877     if(msg->hfield_dat) {
878         FREE(msg->hfield_dat);
879         msg->hfield_dat=NULL;
880     }
881 }
882 
883 /****************************************************************************/
884 /* Copies memory allocated for 'srcmsg' to 'msg'                            */
885 /****************************************************************************/
smb_copymsgmem(smbmsg_t * msg,smbmsg_t * srcmsg)886 int SMBCALL smb_copymsgmem(smbmsg_t* msg, smbmsg_t* srcmsg)
887 {
888     int i;
889 
890     memcpy(msg,srcmsg,sizeof(smbmsg_t));
891 
892     /* data field types/lengths */
893     if((msg->dfield=(dfield_t *)MALLOC(msg->hdr.total_dfields*sizeof(dfield_t)))==NULL)
894         return(1);
895     memcpy(msg->dfield,srcmsg->dfield,msg->hdr.total_dfields*sizeof(dfield_t));
896 
897     /* header field types/lengths */
898     if((msg->hfield=(hfield_t *)MALLOC(msg->total_hfields*sizeof(hfield_t)))==NULL)
899         return(2);
900     memcpy(msg->hfield,srcmsg->hfield,msg->total_hfields*sizeof(hfield_t));
901 
902     /* header field data */
903     if((msg->hfield_dat=(void* *)MALLOC(msg->total_hfields*sizeof(void*)))==NULL)
904         return(3);
905 
906     for(i=0;i<msg->total_hfields;i++) {
907         if((msg->hfield_dat[i]=(char*)MALLOC(msg->hfield[i].length))==NULL)
908             return(4);
909         memcpy(msg->hfield_dat[i],srcmsg->hfield_dat[i],msg->hfield[i].length);
910     }
911 
912     return(0);
913 }
914 
915 /****************************************************************************/
916 /* Unlocks header for 'msg'                                                 */
917 /****************************************************************************/
smb_unlockmsghdr(smb_t * smb,smbmsg_t * msg)918 int SMBCALL smb_unlockmsghdr(smb_t* smb, smbmsg_t* msg)
919 {
920     if(smb->shd_fp==NULL) {
921         sprintf(smb->last_error,"msgbase not open");
922         return(SMB_ERR_NOT_OPEN);
923     }
924     return(unlock(fileno(smb->shd_fp),msg->idx.offset,sizeof(msghdr_t)));
925 }
926 
927 
928 /****************************************************************************/
929 /* Adds a header field to the 'msg' structure (in memory only)              */
930 /****************************************************************************/
smb_hfield(smbmsg_t * msg,uint16_t type,size_t length,void * data)931 int SMBCALL smb_hfield(smbmsg_t* msg, uint16_t type, size_t length, void* data)
932 {
933     hfield_t* vp;
934     void* *vpp;
935     int i;
936 
937     i=msg->total_hfields;
938     if((vp=(hfield_t *)REALLOC(msg->hfield,sizeof(hfield_t)*(i+1)))==NULL)
939         return(1);
940 
941     msg->hfield=vp;
942     if((vpp=(void* *)REALLOC(msg->hfield_dat,sizeof(void* )*(i+1)))==NULL)
943         return(2);
944 
945     msg->hfield_dat=vpp;
946     msg->total_hfields++;
947     msg->hfield[i].type=type;
948     msg->hfield[i].length=length;
949     if(length) {
950         if((msg->hfield_dat[i]=(void* )MALLOC(length))==NULL)
951             return(4);
952         memcpy(msg->hfield_dat[i],data,length);
953     }
954     else
955         msg->hfield_dat[i]=NULL;
956     return(0);
957 }
958 
959 /****************************************************************************/
960 /* Searches for a specific header field (by type) and returns it            */
961 /****************************************************************************/
smb_get_hfield(smbmsg_t * msg,uint16_t type,hfield_t * hfield)962 void* SMBCALL smb_get_hfield(smbmsg_t* msg, uint16_t type, hfield_t* hfield)
963 {
964     int i;
965 
966     for(i=0;i<msg->total_hfields;i++)
967         if(msg->hfield[i].type == type) {
968             if(hfield != NULL)
969                 hfield = &msg->hfield[i];
970             return(msg->hfield_dat[i]);
971         }
972 
973     return(NULL);
974 }
975 
976 /****************************************************************************/
977 /* Adds a data field to the 'msg' structure (in memory only)                */
978 /* Automatically figures out the offset into the data buffer from existing  */
979 /* dfield lengths                                                           */
980 /****************************************************************************/
smb_dfield(smbmsg_t * msg,uint16_t type,uint32_t length)981 int SMBCALL smb_dfield(smbmsg_t* msg, uint16_t type, uint32_t length)
982 {
983     dfield_t* vp;
984     int i,j;
985 
986     i=msg->hdr.total_dfields;
987     if((vp=(dfield_t *)REALLOC(msg->dfield,sizeof(dfield_t)*(i+1)))==NULL)
988         return(1);
989 
990     msg->dfield=vp;
991     msg->hdr.total_dfields++;
992     msg->dfield[i].type=type;
993     msg->dfield[i].length=length;
994     for(j=msg->dfield[i].offset=0;j<i;j++)
995         msg->dfield[i].offset+=msg->dfield[j].length;
996     return(0);
997 }
998 
999 /****************************************************************************/
1000 /* Checks CRC history file for duplicate crc. If found, returns 1.          */
1001 /* If no dupe, adds to CRC history and returns 0, or negative if error.     */
1002 /****************************************************************************/
smb_addcrc(smb_t * smb,uint32_t crc)1003 int SMBCALL smb_addcrc(smb_t* smb, uint32_t crc)
1004 {
1005     char     str[128];
1006     int      file;
1007     int32_t  length;
1008     uint32_t l, *buf;
1009     time32_t start = 0;
1010 
1011     if(!smb->status.max_crcs)
1012         return(0);
1013 
1014     sprintf(str,"%s.sch",smb->file);
1015     while(1) {
1016         if((file=sopen(str,O_RDWR|O_CREAT|O_BINARY,SH_DENYRW,U_MODE))!=-1)
1017             break;
1018         if(errno!=EACCES && errno!=EAGAIN) {
1019             sprintf(smb->last_error,"%d opening %s", errno, str);
1020             return(-1);
1021         }
1022         if(!start)
1023             start = gtime(NULL);
1024         else
1025             if(gtime(NULL)-start >= smb->retry_time) {
1026                 sprintf(smb->last_error,"timeout opening %s (retry_time=%d)"
1027                     ,str,smb->retry_time);
1028                 return(-2);
1029             }
1030         usleep(smb->retry_delay);
1031     }
1032 
1033     length=filelength(file);
1034     if(length<0L) {
1035         close(file);
1036         sprintf(smb->last_error,"invalid file length: %d", length);
1037         return(-4);
1038     }
1039     if((buf=(uint32_t*)MALLOC(smb->status.max_crcs*4))==NULL) {
1040         close(file);
1041         sprintf(smb->last_error
1042             ,"malloc failure of %d bytes"
1043             ,smb->status.max_crcs*4);
1044         return(-3);
1045     }
1046     if((uint32_t)length>=smb->status.max_crcs*4L) { /* Reached or exceeds max crcs */
1047         read(file,buf,smb->status.max_crcs*4);
1048         for(l=0;l<smb->status.max_crcs;l++)
1049             if(crc==buf[l])
1050                 break;
1051         if(l<smb->status.max_crcs) {                /* Dupe CRC found */
1052             close(file);
1053             FREE(buf);
1054             sprintf(smb->last_error
1055                 ,"duplicate message detected");
1056             return(1);
1057         }
1058         chsize(file,0L);                /* truncate it */
1059         lseek(file,0L,SEEK_SET);
1060         write(file,buf+4,(smb->status.max_crcs-1)*4);
1061     }
1062 
1063     else if(length/4) {                     /* Less than max crcs */
1064         read(file,buf,length);
1065         for(l=0;l<(uint32_t)(length/4);l++)
1066             if(crc==buf[l])
1067                 break;
1068         if(l<(uint32_t)(length/4L)) {                   /* Dupe CRC found */
1069             close(file);
1070             FREE(buf);
1071             sprintf(smb->last_error
1072                 ,"duplicate message detected");
1073             return(1);
1074         }
1075     }
1076 
1077     lseek(file,0L,SEEK_END);
1078     write(file,&crc,sizeof(crc));              /* Write to the end */
1079     FREE(buf);
1080     close(file);
1081     return(0);
1082 }
1083 
1084 /****************************************************************************/
1085 /* Creates a new message header record in the header file.                  */
1086 /* If storage is SMB_SELFPACK, self-packing conservative allocation is used */
1087 /* If storage is SMB_FASTALLOC, fast allocation is used                     */
1088 /* If storage is SMB_HYPERALLOC, no allocation tables are used (fastest)    */
1089 /* This function will UN-lock the SMB header                                */
1090 /****************************************************************************/
smb_addmsghdr(smb_t * smb,smbmsg_t * msg,int storage)1091 int SMBCALL smb_addmsghdr(smb_t* smb, smbmsg_t* msg, int storage)
1092 {
1093     int i;
1094     int32_t l;
1095 
1096     if(!smb->locked && smb_locksmbhdr(smb))
1097         return(1);
1098     if(smb_getstatus(smb)) {
1099         smb_unlocksmbhdr(smb);
1100         return(2);
1101     }
1102 
1103     if(storage!=SMB_HYPERALLOC && (i=smb_open_ha(smb))!=0) {
1104         smb_unlocksmbhdr(smb);
1105         return(i);
1106     }
1107 
1108     msg->hdr.length=smb_getmsghdrlen(msg);
1109     if(storage==SMB_HYPERALLOC)
1110         l=smb_hallochdr(smb);
1111     else if(storage==SMB_FASTALLOC)
1112         l=smb_fallochdr(smb,msg->hdr.length);
1113     else
1114         l=smb_allochdr(smb,msg->hdr.length);
1115     if(storage!=SMB_HYPERALLOC)
1116         smb_close_ha(smb);
1117     if(l==-1L) {
1118         smb_unlocksmbhdr(smb);
1119         return(-1);
1120     }
1121 
1122     msg->idx.number=msg->hdr.number=smb->status.last_msg+1;
1123     msg->idx.offset=smb->status.header_offset+l;
1124     msg->idx.time=msg->hdr.when_imported.time;
1125     msg->idx.attr=msg->hdr.attr;
1126     msg->offset=smb->status.total_msgs;
1127     i=smb_putmsg(smb,msg);
1128     if(i==0) {  /* success */
1129         smb->status.last_msg++;
1130         smb->status.total_msgs++;
1131         smb_putstatus(smb);
1132     }
1133     smb_unlocksmbhdr(smb);
1134     return(i);
1135 }
1136 
1137 /****************************************************************************/
1138 /* Writes both header and index information for msg 'msg'                   */
1139 /****************************************************************************/
smb_putmsg(smb_t * smb,smbmsg_t * msg)1140 int SMBCALL smb_putmsg(smb_t* smb, smbmsg_t* msg)
1141 {
1142     int i;
1143 
1144     i=smb_putmsghdr(smb,msg);
1145     if(i)
1146         return(i);
1147     return(smb_putmsgidx(smb,msg));
1148 }
1149 
1150 /****************************************************************************/
1151 /* Writes index information for 'msg'                                       */
1152 /* msg->idx                                                                 */
1153 /* and msg->offset must be set prior to calling to this function            */
1154 /* Returns 0 if everything ok                                               */
1155 /****************************************************************************/
smb_putmsgidx(smb_t * smb,smbmsg_t * msg)1156 int SMBCALL smb_putmsgidx(smb_t* smb, smbmsg_t* msg)
1157 {
1158     if(smb->sid_fp==NULL) {
1159         sprintf(smb->last_error,"index not open");
1160         return(SMB_ERR_NOT_OPEN);
1161     }
1162     clearerr(smb->sid_fp);
1163     fseek(smb->sid_fp,msg->offset*sizeof(idxrec_t),SEEK_SET);
1164     if(!fwrite(&msg->idx,sizeof(idxrec_t),1,smb->sid_fp)) {
1165         sprintf(smb->last_error,"writing index");
1166         return(1);
1167     }
1168     fflush(smb->sid_fp);
1169     return(0);
1170 }
1171 
1172 /****************************************************************************/
1173 /* Writes header information for 'msg'                                      */
1174 /* msg->hdr.length                                                          */
1175 /* msg->idx.offset                                                          */
1176 /* and msg->offset must be set prior to calling to this function            */
1177 /* Returns 0 if everything ok                                               */
1178 /****************************************************************************/
smb_putmsghdr(smb_t * smb,smbmsg_t * msg)1179 int SMBCALL smb_putmsghdr(smb_t* smb, smbmsg_t* msg)
1180 {
1181     uint16_t  i;
1182     uint32_t  l;
1183 
1184     if(smb->shd_fp==NULL) {
1185         sprintf(smb->last_error,"msgbase not open");
1186         return(SMB_ERR_NOT_OPEN);
1187     }
1188     if(msg->idx.offset<sizeof(smbhdr_t)+sizeof(smbstatus_t)
1189         || msg->idx.offset<smb->status.header_offset) {
1190         sprintf(smb->last_error,"invalid header offset: %d",msg->idx.offset);
1191         return(-7);
1192     }
1193     clearerr(smb->shd_fp);
1194     if(fseek(smb->shd_fp,msg->idx.offset,SEEK_SET)) {
1195         sprintf(smb->last_error,"seeking to %d in index",msg->idx.offset);
1196         return(-1);
1197     }
1198 
1199     /**********************************/
1200     /* Set the message header ID here */
1201     /**********************************/
1202     memcpy(&msg->hdr.id,SHD_HEADER_ID,LEN_HEADER_ID);
1203 
1204     /************************************************/
1205     /* Write the fixed portion of the header record */
1206     /************************************************/
1207     if(!fwrite(&msg->hdr,sizeof(msghdr_t),1,smb->shd_fp)) {
1208         sprintf(smb->last_error,"writing fixed portion of header record");
1209         return(-2);
1210     }
1211 
1212     /************************************************/
1213     /* Write the data fields (each is fixed length) */
1214     /************************************************/
1215     for(i=0;i<msg->hdr.total_dfields;i++)
1216         if(!fwrite(&msg->dfield[i],sizeof(dfield_t),1,smb->shd_fp)) {
1217             sprintf(smb->last_error,"writing data field");
1218             return(-3);
1219         }
1220 
1221     /*******************************************/
1222     /* Write the variable length header fields */
1223     /*******************************************/
1224     for(i=0;i<msg->total_hfields;i++) {
1225         if(!fwrite(&msg->hfield[i],sizeof(hfield_t),1,smb->shd_fp)) {
1226             sprintf(smb->last_error,"writing header field");
1227             return(-4);
1228         }
1229         if(msg->hfield[i].length                     /* more then 0 bytes int32_t */
1230             && !fwrite(msg->hfield_dat[i],msg->hfield[i].length,1,smb->shd_fp)) {
1231             sprintf(smb->last_error,"writing header field data");
1232             return(-5);
1233         }
1234     }
1235 
1236     l=smb_getmsghdrlen(msg);
1237     while(l%SHD_BLOCK_LEN) {
1238         if(fputc(0,smb->shd_fp)==EOF) {
1239             sprintf(smb->last_error,"padding header block");
1240             return(-6);                /* pad block with NULL */
1241         }
1242         l++;
1243     }
1244     fflush(smb->shd_fp);
1245     return(0);
1246 }
1247 
1248 /****************************************************************************/
1249 /* Creates a sub-board's initial header file                                */
1250 /* Truncates and deletes other associated SMB files                         */
1251 /****************************************************************************/
smb_create(smb_t * smb)1252 int SMBCALL smb_create(smb_t* smb)
1253 {
1254     char        str[128];
1255     smbhdr_t    hdr;
1256 
1257     if(smb->shd_fp==NULL || smb->sdt_fp==NULL || smb->sid_fp==NULL) {
1258         sprintf(smb->last_error,"msgbase not open");
1259         return(SMB_ERR_NOT_OPEN);
1260     }
1261     if(filelength(fileno(smb->shd_fp))>=sizeof(smbhdr_t)+sizeof(smbstatus_t)
1262         && smb_locksmbhdr(smb))  /* header exists, so lock it */
1263         return(1);
1264     memset(&hdr,0,sizeof(smbhdr_t));
1265     memcpy(hdr.id,SMB_HEADER_ID,LEN_HEADER_ID);
1266     hdr.version=SMB_VERSION;
1267     hdr.length=sizeof(smbhdr_t)+sizeof(smbstatus_t);
1268     smb->status.last_msg=smb->status.total_msgs=0;
1269     smb->status.header_offset=sizeof(smbhdr_t)+sizeof(smbstatus_t);
1270     rewind(smb->shd_fp);
1271     fwrite(&hdr,1,sizeof(smbhdr_t),smb->shd_fp);
1272     fwrite(&(smb->status),1,sizeof(smbstatus_t),smb->shd_fp);
1273     rewind(smb->shd_fp);
1274     chsize(fileno(smb->shd_fp),sizeof(smbhdr_t)+sizeof(smbstatus_t));
1275     fflush(smb->shd_fp);
1276 
1277     rewind(smb->sdt_fp);
1278     chsize(fileno(smb->sdt_fp),0L);
1279     rewind(smb->sid_fp);
1280     chsize(fileno(smb->sid_fp),0L);
1281 
1282     sprintf(str,"%s.sda",smb->file);
1283     remove(str);                        /* if it exists, delete it */
1284     sprintf(str,"%s.sha",smb->file);
1285     remove(str);                        /* if it exists, delete it */
1286     sprintf(str,"%s.sch",smb->file);
1287     remove(str);
1288     smb_unlocksmbhdr(smb);
1289     return(0);
1290 }
1291 
1292 /****************************************************************************/
1293 /* Returns number of data blocks required to store "length" amount of data  */
1294 /****************************************************************************/
smb_datblocks(uint32_t length)1295 uint32_t SMBCALL smb_datblocks(uint32_t length)
1296 {
1297     uint32_t blocks;
1298 
1299     blocks=length/SDT_BLOCK_LEN;
1300     if(length%SDT_BLOCK_LEN)
1301         blocks++;
1302     return(blocks);
1303 }
1304 
1305 /****************************************************************************/
1306 /* Returns number of header blocks required to store "length" size header   */
1307 /****************************************************************************/
smb_hdrblocks(uint32_t length)1308 uint32_t SMBCALL smb_hdrblocks(uint32_t length)
1309 {
1310     uint32_t blocks;
1311 
1312     blocks=length/SHD_BLOCK_LEN;
1313     if(length%SHD_BLOCK_LEN)
1314         blocks++;
1315     return(blocks);
1316 }
1317 
1318 /****************************************************************************/
1319 /* Finds unused space in data file based on block allocation table and      */
1320 /* marks space as used in allocation table.                                 */
1321 /* File must be opened read/write DENY ALL                                  */
1322 /* Returns offset to beginning of data (in bytes, not blocks)               */
1323 /* Assumes smb_open_da() has been called                                    */
1324 /* smb_close_da() should be called after                                    */
1325 /* Returns negative on error                                                */
1326 /****************************************************************************/
smb_allocdat(smb_t * smb,uint32_t length,uint16_t headers)1327 int32_t SMBCALL smb_allocdat(smb_t* smb, uint32_t length, uint16_t headers)
1328 {
1329     uint16_t  i, j;
1330     uint32_t  l, blocks, offset = 0;
1331 
1332     if(smb->sda_fp==NULL) {
1333         sprintf(smb->last_error,"msgbase not open");
1334         return(SMB_ERR_NOT_OPEN);
1335     }
1336     blocks=smb_datblocks(length);
1337     j=0;    /* j is consecutive unused block counter */
1338     fflush(smb->sda_fp);
1339     rewind(smb->sda_fp);
1340     while(!feof(smb->sda_fp)) {
1341         if(!fread(&i,2,1,smb->sda_fp))
1342             break;
1343         offset+=SDT_BLOCK_LEN;
1344         if(!i) j++;
1345         else   j=0;
1346         if(j==blocks) {
1347             offset-=(blocks*SDT_BLOCK_LEN);
1348             break;
1349         }
1350     }
1351     clearerr(smb->sda_fp);
1352     fseek(smb->sda_fp,(offset/SDT_BLOCK_LEN)*2L,SEEK_SET);
1353     for(l=0;l<blocks;l++)
1354         if(!fwrite(&headers,2,1,smb->sda_fp)) {
1355             sprintf(smb->last_error,"writing allocation bytes");
1356             return(-1);
1357         }
1358     fflush(smb->sda_fp);
1359     return(offset);
1360 }
1361 
1362 /****************************************************************************/
1363 /* Allocates space for data, but doesn't search for unused blocks           */
1364 /* Returns negative on error                                                */
1365 /****************************************************************************/
smb_fallocdat(smb_t * smb,uint32_t length,uint16_t headers)1366 int32_t SMBCALL smb_fallocdat(smb_t* smb, uint32_t length, uint16_t headers)
1367 {
1368     uint32_t    l,blocks,offset;
1369 
1370     if(smb->sda_fp==NULL) {
1371         sprintf(smb->last_error,"msgbase not open");
1372         return(SMB_ERR_NOT_OPEN);
1373     }
1374     fflush(smb->sda_fp);
1375     clearerr(smb->sda_fp);
1376     blocks=smb_datblocks(length);
1377     fseek(smb->sda_fp,0L,SEEK_END);
1378     offset=(ftell(smb->sda_fp)/2L)*SDT_BLOCK_LEN;
1379     for(l=0;l<blocks;l++)
1380         if(!fwrite(&headers,2,1,smb->sda_fp))
1381             break;
1382     fflush(smb->sda_fp);
1383     if(l<blocks) {
1384         sprintf(smb->last_error,"writing allocation bytes");
1385         return(-1L);
1386     }
1387     return(offset);
1388 }
1389 
1390 /****************************************************************************/
1391 /* De-allocates space for data                                              */
1392 /* Returns non-zero on error                                                */
1393 /****************************************************************************/
smb_freemsgdat(smb_t * smb,uint32_t offset,uint32_t length,uint16_t headers)1394 int SMBCALL smb_freemsgdat(smb_t* smb, uint32_t offset, uint32_t length, uint16_t headers)
1395 {
1396     int       da_opened = 0;
1397     int       retval = 0;
1398     uint16_t  i;
1399     uint32_t  l, blocks;
1400 
1401     if(smb->status.attr&SMB_HYPERALLOC) /* do nothing */
1402         return(0);
1403 
1404     blocks=smb_datblocks(length);
1405 
1406     if(smb->sda_fp==NULL) {
1407         if((i=smb_open_da(smb))!=0)
1408             return(i);
1409         da_opened=1;
1410     }
1411 
1412     clearerr(smb->sda_fp);
1413     for(l=0;l<blocks;l++) {
1414         if(fseek(smb->sda_fp,((offset/SDT_BLOCK_LEN)+l)*2L,SEEK_SET)) {
1415             sprintf(smb->last_error
1416                 ,"seeking to %ld of allocation file"
1417                 ,((offset/SDT_BLOCK_LEN)+l)*2L);
1418             retval=1;
1419             break;
1420         }
1421         if(!fread(&i,2,1,smb->sda_fp)) {
1422             sprintf(smb->last_error,"reading allocation bytes");
1423             retval=2;
1424             break;
1425         }
1426         if(!headers || headers>i)
1427             i=0;            /* don't want to go negative */
1428         else
1429             i-=headers;
1430         if(fseek(smb->sda_fp,-2L,SEEK_CUR)) {
1431             sprintf(smb->last_error,"seeking backwards 2 bytes in allocation file");
1432             retval=3;
1433             break;
1434         }
1435         if(!fwrite(&i,2,1,smb->sda_fp)) {
1436             sprintf(smb->last_error,"writing allocation bytes");
1437             retval=4;
1438             break;
1439         }
1440     }
1441     fflush(smb->sda_fp);
1442     if(da_opened)
1443         smb_close_da(smb);
1444     return(retval);
1445 }
1446 
1447 /****************************************************************************/
1448 /* Adds to data allocation records for blocks starting at 'offset'          */
1449 /* Returns non-zero on error                                                */
1450 /****************************************************************************/
smb_incdat(smb_t * smb,uint32_t offset,uint32_t length,uint16_t headers)1451 int SMBCALL smb_incdat(smb_t* smb, uint32_t offset, uint32_t length, uint16_t headers)
1452 {
1453     uint16_t  i;
1454     uint32_t  l, blocks;
1455 
1456     if(smb->sda_fp==NULL) {
1457         sprintf(smb->last_error,"msgbase not open");
1458         return(SMB_ERR_NOT_OPEN);
1459     }
1460     clearerr(smb->sda_fp);
1461     blocks=smb_datblocks(length);
1462     for(l=0;l<blocks;l++) {
1463         fseek(smb->sda_fp,((offset/SDT_BLOCK_LEN)+l)*2L,SEEK_SET);
1464         if(!fread(&i,2,1,smb->sda_fp)) {
1465             sprintf(smb->last_error,"reading allocation record");
1466             return(1);
1467         }
1468         i+=headers;
1469         fseek(smb->sda_fp,-2L,SEEK_CUR);
1470         if(!fwrite(&i,2,1,smb->sda_fp)) {
1471             sprintf(smb->last_error,"writing allocation record");
1472             return(2);
1473         }
1474     }
1475     fflush(smb->sda_fp);
1476     return(0);
1477 }
1478 
1479 /****************************************************************************/
1480 /* De-allocates blocks for header record                                    */
1481 /* Returns non-zero on error                                                */
1482 /****************************************************************************/
smb_freemsghdr(smb_t * smb,uint32_t offset,uint32_t length)1483 int SMBCALL smb_freemsghdr(smb_t* smb, uint32_t offset, uint32_t length)
1484 {
1485     uint8_t c = 0;
1486     uint32_t    l,blocks;
1487 
1488     if(smb->sha_fp==NULL) {
1489         sprintf(smb->last_error,"msgbase not open");
1490         return(SMB_ERR_NOT_OPEN);
1491     }
1492     clearerr(smb->sha_fp);
1493     blocks=smb_hdrblocks(length);
1494     fseek(smb->sha_fp,offset/SHD_BLOCK_LEN,SEEK_SET);
1495     for(l=0;l<blocks;l++)
1496         if(!fwrite(&c,1,1,smb->sha_fp)) {
1497             sprintf(smb->last_error,"writing allocation record");
1498             return(1);
1499         }
1500     fflush(smb->sha_fp);
1501     return(0);
1502 }
1503 
1504 /****************************************************************************/
1505 /* Frees all allocated header and data blocks for 'msg'                     */
1506 /****************************************************************************/
smb_freemsg(smb_t * smb,smbmsg_t * msg)1507 int SMBCALL smb_freemsg(smb_t* smb, smbmsg_t* msg)
1508 {
1509     int       i;
1510     uint16_t  x;
1511 
1512     if(smb->status.attr&SMB_HYPERALLOC)  /* Nothing to do */
1513         return(0);
1514 
1515     for(x=0;x<msg->hdr.total_dfields;x++) {
1516         if((i=smb_freemsgdat(smb,msg->hdr.offset+msg->dfield[x].offset
1517             ,msg->dfield[x].length,1))!=0)
1518             return(i);
1519     }
1520     return(smb_freemsghdr(smb,msg->idx.offset-smb->status.header_offset
1521         ,msg->hdr.length));
1522 }
1523 
1524 /****************************************************************************/
1525 /* Finds unused space in header file based on block allocation table and    */
1526 /* marks space as used in allocation table.                                 */
1527 /* File must be opened read/write DENY ALL                                  */
1528 /* Returns offset to beginning of header (in bytes, not blocks)             */
1529 /* Assumes smb_open_ha() has been called                                    */
1530 /* smb_close_ha() should be called after                                    */
1531 /* Returns -1L on error                                                     */
1532 /****************************************************************************/
smb_allochdr(smb_t * smb,uint32_t length)1533 int32_t SMBCALL smb_allochdr(smb_t* smb, uint32_t length)
1534 {
1535     uint8_t   c;
1536     uint16_t  i;
1537     uint32_t  l, blocks, offset = 0;
1538 
1539     if(smb->sha_fp==NULL) {
1540         sprintf(smb->last_error,"msgbase not open");
1541         return(SMB_ERR_NOT_OPEN);
1542     }
1543     blocks=smb_hdrblocks(length);
1544     i=0;    /* i is consecutive unused block counter */
1545     fflush(smb->sha_fp);
1546     rewind(smb->sha_fp);
1547     while(!feof(smb->sha_fp)) {
1548         if(!fread(&c,1,1,smb->sha_fp))
1549             break;
1550         offset+=SHD_BLOCK_LEN;
1551         if(!c) i++;
1552         else   i=0;
1553         if(i==blocks) {
1554             offset-=(blocks*SHD_BLOCK_LEN);
1555             break;
1556         }
1557     }
1558     clearerr(smb->sha_fp);
1559     fseek(smb->sha_fp,offset/SHD_BLOCK_LEN,SEEK_SET);
1560     c=1;
1561     for(l=0;l<blocks;l++)
1562         if(!fwrite(&c,1,1,smb->sha_fp)) {
1563             sprintf(smb->last_error,"writing allocation record");
1564             return(-1L);
1565         }
1566     fflush(smb->sha_fp);
1567     return(offset);
1568 }
1569 
1570 /****************************************************************************/
1571 /* Allocates space for index, but doesn't search for unused blocks          */
1572 /* Returns -1L on error                                                     */
1573 /****************************************************************************/
smb_fallochdr(smb_t * smb,uint32_t length)1574 int32_t SMBCALL smb_fallochdr(smb_t* smb, uint32_t length)
1575 {
1576     uint8_t c = 1;
1577     uint32_t    l,blocks,offset;
1578 
1579     if(smb->sha_fp==NULL) {
1580         sprintf(smb->last_error,"msgbase not open");
1581         return(SMB_ERR_NOT_OPEN);
1582     }
1583     blocks=smb_hdrblocks(length);
1584     fflush(smb->sha_fp);
1585     clearerr(smb->sha_fp);
1586     fseek(smb->sha_fp,0L,SEEK_END);
1587     offset=ftell(smb->sha_fp)*SHD_BLOCK_LEN;
1588     for(l=0;l<blocks;l++)
1589         if(!fwrite(&c,1,1,smb->sha_fp)) {
1590             sprintf(smb->last_error,"writing allocation record");
1591             return(-1L);
1592         }
1593     fflush(smb->sha_fp);
1594     return(offset);
1595 }
1596 
1597 /************************************************************************/
1598 /* Allocate header blocks using Hyper Allocation                        */
1599 /* this function should be most likely not be called from anywhere but  */
1600 /* smb_addmsghdr()                                                      */
1601 /************************************************************************/
smb_hallochdr(smb_t * smb)1602 int32_t SMBCALL smb_hallochdr(smb_t* smb)
1603 {
1604     uint32_t l;
1605 
1606     if(smb->shd_fp==NULL) {
1607         sprintf(smb->last_error,"msgbase not open");
1608         return(SMB_ERR_NOT_OPEN);
1609     }
1610     fflush(smb->shd_fp);
1611     fseek(smb->shd_fp,0L,SEEK_END);
1612     l=ftell(smb->shd_fp);
1613     if(l<smb->status.header_offset)              /* Header file truncated?!? */
1614         return(smb->status.header_offset);
1615     while((l-smb->status.header_offset)%SHD_BLOCK_LEN)  /* Even block boundry */
1616         l++;
1617     return(l-smb->status.header_offset);
1618 }
1619 
1620 /************************************************************************/
1621 /* Allocate data blocks using Hyper Allocation                          */
1622 /* smb_locksmbhdr() should be called before this function and not       */
1623 /* unlocked until all data fields for this message have been written    */
1624 /* to the SDT file                                                      */
1625 /************************************************************************/
smb_hallocdat(smb_t * smb)1626 int32_t SMBCALL smb_hallocdat(smb_t* smb)
1627 {
1628     int32_t l;
1629 
1630     if(smb->sdt_fp==NULL) {
1631         sprintf(smb->last_error,"msgbase not open");
1632         return(SMB_ERR_NOT_OPEN);
1633     }
1634     fflush(smb->sdt_fp);
1635     fseek(smb->sdt_fp,0L,SEEK_END);
1636     l=ftell(smb->sdt_fp);
1637     if(l<=0)
1638         return(l);
1639     while(l%SDT_BLOCK_LEN)                  /* Make sure even block boundry */
1640         l++;
1641     return(l);
1642 }
1643 
1644 
smb_feof(FILE * fp)1645 int SMBCALL smb_feof(FILE* fp)
1646 {
1647     return(feof(fp));
1648 }
1649 
smb_ferror(FILE * fp)1650 int SMBCALL smb_ferror(FILE* fp)
1651 {
1652     return(ferror(fp));
1653 }
1654 
smb_fflush(FILE * fp)1655 int SMBCALL smb_fflush(FILE* fp)
1656 {
1657     return(fflush(fp));
1658 }
1659 
smb_fgetc(FILE * fp)1660 int SMBCALL smb_fgetc(FILE* fp)
1661 {
1662     return(fgetc(fp));
1663 }
1664 
smb_fputc(int ch,FILE * fp)1665 int SMBCALL smb_fputc(int ch, FILE* fp)
1666 {
1667     return(fputc(ch,fp));
1668 }
1669 
smb_fseek(FILE * fp,int32_t offset,int whence)1670 int SMBCALL smb_fseek(FILE* fp, int32_t offset, int whence)
1671 {
1672     return(fseek(fp,offset,whence));
1673 }
1674 
smb_ftell(FILE * fp)1675 int32_t SMBCALL smb_ftell(FILE* fp)
1676 {
1677     return(ftell(fp));
1678 }
1679 
smb_fgetlength(FILE * fp)1680 int32_t SMBCALL smb_fgetlength(FILE* fp)
1681 {
1682     return(filelength(fileno(fp)));
1683 }
1684 
smb_fsetlength(FILE * fp,int32_t length)1685 int SMBCALL smb_fsetlength(FILE* fp, int32_t length)
1686 {
1687     return(chsize(fileno(fp),length));
1688 }
1689 
smb_rewind(FILE * fp)1690 void SMBCALL smb_rewind(FILE* fp)
1691 {
1692     rewind(fp);
1693 }
1694 
smb_clearerr(FILE * fp)1695 void SMBCALL smb_clearerr(FILE* fp)
1696 {
1697     clearerr(fp);
1698 }
1699 
smb_fread(void HUGE16 * buf,int32_t bytes,FILE * fp)1700 int32_t SMBCALL smb_fread(void HUGE16* buf, int32_t bytes, FILE* fp)
1701 {
1702 #ifdef __FLAT__
1703     return(fread(buf,1,bytes,fp));
1704 #else
1705     int32_t count;
1706 
1707     for(count=bytes;count>0x7fff;count-=0x7fff,(char*)buf+=0x7fff)
1708         if(fread((char*)buf,1,0x7fff,fp)!=0x7fff)
1709             return(bytes-count);
1710     if(fread((char*)buf,1,(size_t)count,fp)!=(size_t)count)
1711         return(bytes-count);
1712     return(bytes);
1713 #endif
1714 }
1715 
smb_fwrite(void HUGE16 * buf,int32_t bytes,FILE * fp)1716 int32_t SMBCALL smb_fwrite(void HUGE16* buf, int32_t bytes, FILE* fp)
1717 {
1718 #ifdef __FLAT__
1719     return(fwrite(buf,1,bytes,fp));
1720 #else
1721     int32_t count;
1722 
1723     for(count=bytes;count>0x7fff;count-=0x7fff,(char*)buf+=0x7fff)
1724         if(fwrite((char*)buf,1,0x7fff,fp)!=0x7fff)
1725             return(bytes-count);
1726     if(fwrite((char*)buf,1,(size_t)count,fp)!=(size_t)count)
1727         return(bytes-count);
1728     return(bytes);
1729 #endif
1730 }
1731 
1732 /* End of SMBLIB.C */
1733