1 /* $Id$ */
2 /*****************************************************************************
3 * SqPack --- FTN messagebase packer (purger)
4 *****************************************************************************
5 * Copyright (C) 1997-1999 Matthias Tichy (mtt@tichy.de).
6 * Copyright (C) 1999-2002 Husky developers team
7 *
8 * This file is part of HUSKY Fidonet Software project.
9 *
10 * SQPACK is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2, or (at your option) any
13 * later version.
14 *
15 * SQPACK is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with HPT; see the file COPYING.  If not, write to the Free
22 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *****************************************************************************
24 */
25 
26 #include <stdio.h>
27 #include <errno.h>
28 #include <time.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 
35 #include <huskylib/compiler.h>
36 #include <huskylib/huskylib.h>
37 #include <huskylib/locking.h>
38 #include <huskylib/strext.h>
39 
40 #ifdef HAS_UNISTD_H
41 #include <unistd.h>
42 #endif
43 
44 #ifdef HAS_IO_H
45 #include <io.h>
46 #endif
47 
48 #ifdef HAS_SHARE_H
49 #include <share.h>
50 #endif
51 
52 #include <smapi/msgapi.h>
53 #include <fidoconf/fidoconf.h>
54 #include <fidoconf/common.h>
55 #include <huskylib/log.h>
56 #include <huskylib/xstr.h>
57 
58 #include "version.h"
59 
60 #define LOGFILE "sqpack.log"
61 
62 unsigned long msgCopied, msgProcessed; /*  per Area */
63 unsigned long totaloldMsg, totalmsgCopied;
64 unsigned long totalOldBaseSize, totalNewBaseSize;
65 int lock_fd;
66 char *versionStr;
67 int area_found;
68 s_fidoconfig *config;
69 
70 
SqReadLastreadFile(char * fileName,UINT32 ** lastreadp,ULONG * lcountp,HAREA area)71 void SqReadLastreadFile(char *fileName, UINT32 **lastreadp, ULONG *lcountp,
72                         HAREA area)
73 {
74     int fd;
75     struct stat st;
76     unsigned long i, temp;
77     unsigned char buffer[4];
78     char *name=NULL;
79 
80     w_log(LL_FUNC, "SqReadLastreadFile() begin");
81 
82     xstrscat( &name, fileName,  ".sql" , NULL);
83 
84     fd = sopen(name, O_BINARY | O_RDWR, SH_DENYNO, S_IWRITE | S_IREAD);
85     if (fd != -1) {
86 
87         fstat(fd, &st);
88         *lcountp = st.st_size / 4;
89         *lastreadp = (UINT32 *) malloc(*lcountp * sizeof(UINT32));
90 
91         for (i = 0; i < *lcountp; i++) {
92             read(fd, &buffer, 4);
93             temp = buffer[0] + (((unsigned long)(buffer[1])) << 8) +
94                 (((unsigned long)(buffer[2])) << 16) +
95                 (((unsigned long)(buffer[3])) << 24);
96             (*lastreadp)[i] = MsgUidToMsgn(area, temp, UID_PREV);
97         }
98 
99         close(fd);
100 
101     } else {
102         *lastreadp = NULL;
103         *lcountp = 0;
104     };
105 
106     nfree(name);
107     w_log(LL_FUNC, "SqReadLastreadFile() end");
108 }
109 
110 
SqWriteLastreadFile(char * fileName,UINT32 * lastread,ULONG lcount,HAREA area)111 void SqWriteLastreadFile(char *fileName, UINT32 *lastread, ULONG lcount,
112                          HAREA area)
113 {
114     char *name=NULL;
115     unsigned char buffer[4];
116     int fd;
117     unsigned long i, temp;
118 
119     w_log(LL_FUNC, "SqWriteLastreadFile() begin");
120     if (lastread) {
121 
122         xstrscat( &name, fileName,  ".sql" , NULL );
123 
124         fd = sopen(name, O_BINARY | O_RDWR, SH_DENYNO, S_IWRITE | S_IREAD);
125 
126         if (fd != -1) {
127 
128             lseek(fd, 0l, SEEK_SET);
129 
130             for (i = 0; i < lcount; i++) {
131 
132                 temp = MsgMsgnToUid(area, lastread[i]);
133 
134                 buffer[0] = (UCHAR)(  temp & 0xFF);
135                 buffer[1] = (UCHAR)( (temp >> 8) & 0xFF );
136                 buffer[2] = (UCHAR)( (temp >> 16) & 0xFF );
137                 buffer[3] = (UCHAR)( (temp >> 24) & 0xFF );
138 
139                 write(fd, &buffer, 4);
140             }
141 
142             close(fd);
143 
144         } else
145             w_log(LL_ERR, "Could not write lastread file '%s': %s", name, strerror(errno));
146 
147         nfree(name);
148     }
149     w_log(LL_FUNC, "SqWriteLastreadFile() end");
150 }
151 
152 
153 typedef struct
154 {
155     unsigned long UserCRC;         /* CRC-32 of user name (lowercase) */
156     unsigned long UserID;          /* Unique UserID */
157     unsigned long LastReadMsg;     /* Last read message number */
158     unsigned long HighReadMsg;     /* Highest read message number */
159 }
160 JAMLREAD;
161 #define JAMLREAD_SIZE 16
162 
read_jamlread(int fd,JAMLREAD * plread)163 int read_jamlread(int fd, JAMLREAD *plread)
164 {
165     unsigned char buf[JAMLREAD_SIZE];
166 
167     w_log(LL_FUNC, "read_jamlread() begin");
168     if (read(fd, buf, JAMLREAD_SIZE) != JAMLREAD_SIZE) {
169         w_log(LL_ERR, "read_jamlread() error: %s", strerror(errno));
170         w_log(LL_FUNC, "read_jamlread() failed");
171         return 0;
172     }
173 
174     plread->UserCRC     = get_dword(buf);
175     plread->UserID      = get_dword(buf+4);
176     plread->LastReadMsg = get_dword(buf+8);
177     plread->HighReadMsg = get_dword(buf+12);
178 
179     w_log(LL_FUNC, "read_jamlread() OK");
180     return 1;
181 }
182 
write_jamlread(int fd,JAMLREAD * plread)183 int write_jamlread(int fd, JAMLREAD *plread)
184 {
185     unsigned char buf[JAMLREAD_SIZE];
186 
187     w_log(LL_FUNC, "write_jamlread() begin");
188     put_dword(buf, plread->UserCRC);
189     put_dword(buf + 4, plread->UserID);
190     put_dword(buf + 8, plread->LastReadMsg);
191     put_dword(buf + 12, plread->HighReadMsg);
192 
193     if (write(fd, buf, JAMLREAD_SIZE) != JAMLREAD_SIZE) {
194         w_log(LL_ERR, "write_jamlread() error: %s", strerror(errno));
195         w_log(LL_FUNC, "write_jamlread() failed");
196         return 0;
197     }
198 
199     w_log(LL_FUNC, "write_jamlread() OK");
200     return 1;
201 }
202 
write_partial_jamlread(int fd,JAMLREAD * plread)203 int write_partial_jamlread(int fd, JAMLREAD *plread)
204 {
205     unsigned char buf[JAMLREAD_SIZE/2];
206 
207     w_log(LL_FUNC, "write_partial_jamlread() begin");
208     put_dword(buf + 0, plread->LastReadMsg);
209     put_dword(buf + 4, plread->HighReadMsg);
210 
211     if (write(fd, buf, JAMLREAD_SIZE/2) != JAMLREAD_SIZE/2) {
212         w_log(LL_ERR, "write_partial_jamlread() error: %s", strerror(errno));
213         w_log(LL_FUNC, "write_partial_jamlread() failed");
214         return 0;
215     }
216 
217     w_log(LL_FUNC, "write_partial_jamlread() OK");
218     return 1;
219 }
220 
JamReadLastreadFile(char * fileName,UINT32 ** lastreadp,ULONG * lcountp,HAREA area)221 void JamReadLastreadFile(char *fileName, UINT32 **lastreadp, ULONG *lcountp,
222                          HAREA area)
223 {
224     int fd;
225     struct stat st;
226     unsigned long i;
227     char *name = NULL;
228     JAMLREAD lread;
229 
230     w_log(LL_FUNC, "JamReadLastreadFile() begin");
231 
232     xstrscat( &name, fileName,  ".jlr" , NULL);
233 
234     fd = sopen(name, O_BINARY | O_RDWR, SH_DENYNO, S_IWRITE | S_IREAD);
235     if (fd != -1) {
236 
237         fstat(fd, &st);
238         *lcountp = st.st_size / JAMLREAD_SIZE;
239         *lastreadp = (UINT32 *) malloc(*lcountp * sizeof(UINT32) * 2);
240 
241         for (i = 0; i < *lcountp; i++) {
242             read_jamlread(fd, &lread);
243             (*lastreadp)[i*2] = MsgUidToMsgn(area, lread.LastReadMsg, UID_PREV);
244             (*lastreadp)[i*2+1] = MsgUidToMsgn(area, lread.HighReadMsg, UID_PREV);
245         }
246 
247         close(fd);
248 
249     } else {
250         w_log(LL_ERR, "JamReadLastreadFile(): can't open %s: %s", name, strerror(errno));
251         *lastreadp = NULL;
252         *lcountp = 0;
253     };
254 
255     *lcountp = (*lcountp) << 1; /* rest of sqpack does not now of 2 lastread ptrs */
256 
257     nfree(name);
258     w_log(LL_FUNC, "JamReadLastreadFile() end");
259 }
260 
JamWriteLastreadFile(char * fileName,UINT32 * lastread,ULONG lcount,HAREA area)261 void JamWriteLastreadFile(char *fileName, UINT32 *lastread, ULONG lcount,
262                           HAREA area)
263 {
264     char *name = NULL;
265     int fd;
266     unsigned long i;
267     JAMLREAD lread;
268 
269     w_log(LL_FUNC, "JamWriteLastreadFile() begin");
270     if (lastread) {
271 
272         xstrscat( &name, fileName,  ".jlr" , NULL);
273 
274         fd = sopen(name, O_BINARY | O_RDWR, SH_DENYNO, S_IWRITE | S_IREAD);
275 
276         if (fd != -1) {
277 
278             for (i = 0; i < (lcount >> 1); i++) {
279 
280                 lread.LastReadMsg = MsgMsgnToUid(area, lastread[i*2]);
281                 lread.HighReadMsg = MsgMsgnToUid(area, lastread[i*2+1]);
282 
283                 lseek(fd, i*JAMLREAD_SIZE + JAMLREAD_SIZE/2, SEEK_SET);
284                 write_partial_jamlread(fd, &lread);
285             }
286 
287             close(fd);
288 
289         } else
290             w_log(LL_ERR, "JamWriteLastreadFile(): can't open %s: %s", name, strerror(errno));
291 
292         nfree(name);
293     }
294     w_log(LL_FUNC, "JamWriteLastreadFile() end");
295 }
296 
SdmReadLastreadFile(char * fileName,UINT32 ** lastreadp,ULONG * lcountp,HAREA area)297 void SdmReadLastreadFile(char *fileName, UINT32 **lastreadp, ULONG *lcountp,
298                          HAREA area)
299 {
300     int fd;
301     struct stat st;
302     unsigned long i;
303     char *name;
304     UINT16 temp;
305 
306     w_log(LL_FUNC, "SdmReadLastreadFile() begin");
307     name = (char *) malloc(strlen(fileName)+9+1);
308     strcpy(name, fileName);
309     Add_Trailing(name, PATH_DELIM);
310     strcat(name, "lastread");
311 
312     fd = sopen(name, O_BINARY | O_RDWR, SH_DENYNO, S_IWRITE | S_IREAD);
313     if (fd != -1) {
314 
315         fstat(fd, &st);
316         *lcountp = st.st_size / 2; /*sizeof(UINT16)*/
317         *lastreadp = (UINT32 *) malloc(*lcountp * sizeof(UINT32));
318 
319         for (i = 0; i < *lcountp; i++) {
320             read(fd, &temp, 2);
321             (*lastreadp)[i] = MsgUidToMsgn(area, temp, UID_PREV);
322         }
323 
324         close(fd);
325 
326     } else {
327         w_log(LL_ERR, "SdmReadLastreadFile(): can't open %s: %s", name, strerror(errno));
328         *lastreadp = NULL;
329         *lcountp = 0;
330     };
331 
332     nfree(name);
333     w_log(LL_FUNC, "SdmReadLastreadFile() end");
334 }
335 
SdmWriteLastreadFile(char * fileName,UINT32 * lastread,ULONG lcount,HAREA area)336 void SdmWriteLastreadFile(char *fileName, UINT32 *lastread, ULONG lcount,
337                           HAREA area)
338 {
339     char *name;
340     int fd;
341     unsigned long i;
342     unsigned char buf[2];
343 
344     unused(area);
345 
346     w_log(LL_FUNC, "SdmWriteLastreadFile() begin");
347     if (lastread) {
348 
349         name = (char *) malloc(strlen(fileName)+9+1);
350         strcpy(name, fileName);
351         Add_Trailing(name, PATH_DELIM);
352         strcat(name, "lastread");
353 
354         fd = sopen(name, O_BINARY | O_RDWR, SH_DENYNO, S_IWRITE | S_IREAD);
355 
356         if (fd != -1) {
357 
358             lseek(fd, 0, SEEK_SET);
359             for (i = 0; i < lcount; i++) {
360 #if 0 /* Messages renumbered, MsgMsgnToUid() returns old value */
361       /* We should reopen area before write lastreads */
362                 UINT16 temp = (UINT16)MsgMsgnToUid(area, lastread[i]);
363                 put_word(buf, temp);
364 #else /* msgns are equial to uids after renumber */
365 		put_word(buf, (UINT16)lastread[i]);
366 #endif
367                 write(fd, buf, 2);
368             }
369 
370             close(fd);
371 
372         } else
373             w_log(LL_ERR, "SdmWriteLastreadFile(): can't open %s: %s", name, strerror(errno));
374 
375         nfree(name);
376     }
377     w_log(LL_FUNC, "SdmWriteLastreadFile() end");
378 #ifdef __WATCOMC__
379     area=area; /* prevent warning */
380 #endif
381 }
382 
readLastreadFile(char * fileName,UINT32 ** lastreadp,ULONG * lcountp,HAREA area,int areaType)383 void readLastreadFile(char *fileName, UINT32 **lastreadp, ULONG *lcountp,
384                       HAREA area, int areaType)
385 {
386     w_log(LL_FUNC, "readLastreadFile() begin");
387     if (areaType == MSGTYPE_SQUISH)
388         SqReadLastreadFile(fileName, lastreadp, lcountp, area);
389     else if (areaType == MSGTYPE_JAM)
390         JamReadLastreadFile(fileName, lastreadp, lcountp, area);
391     else if (areaType == MSGTYPE_SDM)
392         SdmReadLastreadFile(fileName, lastreadp, lcountp, area);
393     w_log(LL_FUNC, "readLastreadFile() end");
394 }
395 
writeLastreadFile(char * fileName,UINT32 * lastreadp,ULONG lcount,HAREA area,int areaType)396 void writeLastreadFile(char *fileName, UINT32 *lastreadp, ULONG lcount,
397                        HAREA area, int areaType)
398 {
399     w_log(LL_FUNC, "writeLastreadFile() begin");
400     if (areaType == MSGTYPE_SQUISH)
401         SqWriteLastreadFile(fileName, lastreadp, lcount, area);
402     else if (areaType == MSGTYPE_JAM)
403         JamWriteLastreadFile(fileName, lastreadp, lcount, area);
404     else if (areaType == MSGTYPE_SDM)
405         SdmWriteLastreadFile(fileName, lastreadp, lcount, area);
406     w_log(LL_FUNC, "writeLastreadFile() end");
407 }
408 
getOffsetInLastread(UINT32 * lastread,ULONG lcount,dword msgnum)409 unsigned long getOffsetInLastread(UINT32 *lastread, ULONG lcount, dword msgnum)
410 {
411 
412     unsigned long i;
413 
414     for (i = 0; i < lcount; i++) {
415         if (lastread[i] == msgnum) return i;
416     }
417 
418     return (unsigned long)(-1);
419 
420 }
421 
422 /* returns zero if msg was killed, nonzero if it was copied */
423 
processMsg(dword msgNum,dword numMsg,HAREA oldArea,HAREA newArea,s_area * area,UINT32 shift)424 int processMsg(dword msgNum, dword numMsg, HAREA oldArea, HAREA newArea,
425                s_area *area, UINT32 shift)
426 {
427     HMSG msg, newMsg;
428     XMSG xmsg;
429     struct tm tmTime;
430     time_t ttime, actualTime = time(NULL);
431     char *text, *ctrlText;
432     dword  textLen, ctrlLen;
433     int unsent, i, rc = 0;
434     unsigned long uid2msgn;
435 
436     /*    unsigned long offset; */
437 
438     w_log(LL_FUNC, "processMsg() begin");
439     msg = MsgOpenMsg(oldArea, MOPEN_RW, msgNum);
440     if (msg == NULL) return rc;
441 
442     if (MsgReadMsg(msg, &xmsg, 0, 0, NULL, 0, NULL)==(dword)-1l) {
443         MsgCloseMsg(msg);
444         msgProcessed++;
445         return rc;
446     }
447 
448     unsent = ((xmsg.attr & MSGLOCAL) && !(xmsg.attr & MSGSENT)) || (xmsg.attr & MSGLOCKED);
449 
450     if ( unsent || (((area -> max == 0) || ((numMsg - msgProcessed + msgCopied) <= area -> max) ||
451         (area -> keepUnread && !(xmsg.attr & MSGREAD))) && !((xmsg.attr & MSGREAD) && area -> killRead))) {
452         /*only max msgs should be in new area*/
453 
454         if (xmsg.attr & MSGLOCAL) {
455             DosDate_to_TmDate((SCOMBO*)&(xmsg.date_written), &tmTime);
456         } else {
457             DosDate_to_TmDate((SCOMBO*)&(xmsg.date_arrived), &tmTime);
458         }
459         /*     DosDate_to_TmDate(&(xmsg.attr & MSGLOCAL ? xmsg.date_written :
460         xmsg.date_arrived), &tmTime);*/
461         ttime = mktime(&tmTime);
462         if (ttime == 0xfffffffflu) ttime = 0; /* emx */
463 
464         if (unsent || (area -> purge == 0) || ttime == 0 ||
465             (labs(actualTime - ttime) <= (long)(area -> purge * 24 *60 * 60))) {
466             uid2msgn = MsgUidToMsgn(oldArea, xmsg.replyto, UID_EXACT);
467             xmsg.replyto =  uid2msgn > shift ? uid2msgn - shift : 0;
468             if ((area->msgbType & MSGTYPE_SQUISH) == MSGTYPE_SQUISH){
469 
470                 for (i = 0; i < MAX_REPLY; i++) {
471                     uid2msgn = MsgUidToMsgn(oldArea, xmsg.replies[i], UID_EXACT);
472                     xmsg.replies[i] = uid2msgn > shift ? uid2msgn - shift : 0;
473                 }
474             }else {
475                 uid2msgn = MsgUidToMsgn(oldArea, xmsg.replies[0], UID_EXACT);
476                 xmsg.replies[0] =  uid2msgn > shift ? uid2msgn - shift : 0;
477                 uid2msgn = MsgUidToMsgn(oldArea, xmsg.xmreplynext, UID_EXACT);
478                 xmsg.xmreplynext = uid2msgn > shift ? uid2msgn - shift : 0;
479             }
480             /*  copy msg */
481             textLen = MsgGetTextLen(msg);
482             ctrlLen = MsgGetCtrlLen(msg);
483 
484             text = (char *) malloc(textLen+1);
485             text[textLen] = '\0';
486 
487             ctrlText = (char *) malloc(ctrlLen+1);
488             ctrlText[ctrlLen] = '\0';
489 
490             MsgReadMsg(msg, NULL, 0, textLen, (byte*)text, ctrlLen, (byte*)ctrlText);
491 
492             if (area->msgbType & MSGTYPE_SDM)
493                 MsgWriteMsg(msg, 0, &xmsg, (byte*)text, textLen, textLen, ctrlLen, (byte*)ctrlText);
494             else {
495                 newMsg = MsgOpenMsg(newArea, MOPEN_CREATE, 0);
496                 MsgWriteMsg(newMsg, 0, &xmsg, (byte*)text, textLen, textLen, ctrlLen, (byte*)ctrlText);
497                 MsgCloseMsg(newMsg);
498             }
499 
500             msgCopied++;
501             nfree(text);
502             nfree(ctrlText);
503             rc = 1;
504         }
505 
506     }
507     MsgCloseMsg(msg);
508     msgProcessed++;
509     w_log(LL_FUNC, "processMsg() end");
510     return rc;
511 }
512 
getShiftedNum(UINT32 msgNum,UINT32 rmCount,UINT32 * rmMap)513 UINT32 getShiftedNum(UINT32 msgNum, UINT32 rmCount, UINT32 *rmMap)
514 {
515     UINT32 i, nMsgNum = msgNum;
516 
517     if (*rmMap == 1) {
518         rmMap += 2;
519         rmCount -= 2;
520     }
521     for (i=0; i<rmCount; i+=2) {
522         if (msgNum < rmMap[i])
523             break;
524         if (msgNum >= rmMap[i] + rmMap[i+1]) {
525             nMsgNum -= rmMap[i+1];
526         } else {
527             return 0L;
528         }
529     }
530     return nMsgNum;
531 }
532 
updateMsgLinks(UINT32 msgNum,HAREA area,UINT32 rmCount,UINT32 * rmMap,int areaType)533 void updateMsgLinks(UINT32 msgNum, HAREA area, UINT32 rmCount, UINT32 *rmMap, int areaType)
534 {
535     HMSG msg;
536     XMSG xmsg;
537     int i;
538 
539     w_log(LL_FUNC, "updateMsgLinks() begin");
540     msg = MsgOpenMsg(area, MOPEN_RW, getShiftedNum(msgNum, rmCount, rmMap));
541     if (msg == NULL) return;
542 
543     MsgReadMsg(msg, &xmsg, 0, 0, NULL, 0, NULL);
544 
545     xmsg.replyto = getShiftedNum(xmsg.replyto, rmCount, rmMap);
546     if ((areaType & MSGTYPE_SQUISH) == MSGTYPE_SQUISH)
547         for (i = 0; i < MAX_REPLY; i++)
548             xmsg.replies[i] = getShiftedNum(xmsg.replies[i], rmCount, rmMap);
549     else {
550         xmsg.replies[0] = getShiftedNum(xmsg.replies[0], rmCount, rmMap);
551         xmsg.xmreplynext = getShiftedNum(xmsg.xmreplynext, rmCount, rmMap);
552     }
553 
554     MsgWriteMsg(msg, 0, &xmsg, NULL, 0, 0, 0, NULL);
555     MsgCloseMsg(msg);
556     w_log(LL_FUNC, "updateMsgLinks() end");
557 }
558 
559 
renameArea(int areaType,char * oldName,char * newName)560 int renameArea(int areaType, char *oldName, char *newName)
561 {
562     char *oldTmp=NULL, *newTmp=NULL;
563     unsigned long oldsize=0, newsize=0;
564     struct stat sb;
565 
566     w_log(LL_FUNC, "renameArea() begin");
567 
568     xstrcat(&oldTmp, oldName);
569     xstrcat(&newTmp, newName);
570 
571     if (areaType==MSGTYPE_SQUISH) {
572         xstrcat(&oldTmp, ".sqd");
573         xstrcat(&newTmp, ".sqd");
574         /* sizes of files: for statistics */
575         stat(oldTmp,&sb);
576         oldsize += sb.st_size;
577         stat(newTmp,&sb);
578         newsize += sb.st_size;
579         remove(oldTmp);
580         rename(newTmp, oldTmp);
581 
582         oldTmp[strlen(oldTmp)-1] = 'i';
583         newTmp[strlen(newTmp)-1] = 'i';
584         /* sizes of files: for statistics */
585         stat(oldTmp,&sb);
586         oldsize += sb.st_size;
587         stat(newTmp,&sb);
588         newsize += sb.st_size;
589         if (remove(oldTmp))
590             return errno;
591         if (rename(newTmp, oldTmp))
592             return errno;
593     }
594 
595     if (areaType==MSGTYPE_JAM) {
596         xstrcat(&oldTmp, ".jdt");
597         xstrcat(&newTmp, ".jdt");
598         /* sizes of files: for statistics */
599         stat(oldTmp,&sb);
600         oldsize += sb.st_size;
601         stat(newTmp,&sb);
602         newsize += sb.st_size;
603 
604         if (remove(oldTmp))
605             return errno;
606         if (rename(newTmp, oldTmp))
607             return errno;
608 
609         oldTmp[strlen(oldTmp)-1] = 'x';
610         newTmp[strlen(newTmp)-1] = 'x';
611         /* sizes of files: for statistics */
612         stat(oldTmp,&sb);
613         oldsize += sb.st_size;
614         stat(newTmp,&sb);
615         newsize += sb.st_size;
616         if (remove(oldTmp))
617             return errno;
618         if (rename(newTmp, oldTmp))
619             return errno;
620 
621         oldTmp[strlen(oldTmp)-2] = 'h';
622         newTmp[strlen(newTmp)-2] = 'h';
623         oldTmp[strlen(oldTmp)-1] = 'r';
624         newTmp[strlen(newTmp)-1] = 'r';
625         /* sizes of files: for statistics */
626         stat(oldTmp,&sb);
627         oldsize += sb.st_size;
628         stat(newTmp,&sb);
629         newsize += sb.st_size;
630         if (remove(oldTmp))
631             return errno;
632         if (rename(newTmp, oldTmp))
633             return errno;
634 
635 /*
636         newTmp[strlen(newTmp)-2] = 'l';
637         oldTmp[strlen(oldTmp)-2] = 'l';
638 
639         if (remove(oldTmp))
640             return errno;
641         if (rename(newTmp, oldTmp))
642             return errno;
643         if (remove(newTmp))
644             return errno;
645 */
646 
647     }
648 
649     w_log(  LL_STAT, "      old size:%10lu; new size:%10lu", oldsize, newsize );
650 
651     totalOldBaseSize+=oldsize, totalNewBaseSize+=newsize;
652     nfree(oldTmp);
653     nfree(newTmp);
654     w_log(LL_FUNC, "renameArea() end");
655     return 0;
656 }
657 
purgeArea(s_area * area)658 void purgeArea(s_area *area)
659 {
660     char *oldName = area -> fileName;
661     char *newName=NULL;
662     HAREA oldArea=NULL, newArea = NULL;
663     dword highMsg, i, j, numMsg, hw=0;
664     int areaType = area -> msgbType & (MSGTYPE_JAM | MSGTYPE_SQUISH | MSGTYPE_SDM);
665 
666     UINT32 *oldLastread, *newLastread = 0;
667     UINT32 *removeMap;
668     UINT32 rmIndex = 0;
669 
670     w_log(LL_FUNC, "purgeArea() begin");
671     if (area->nopack) {
672         printf("   No purging needed!\n");
673         w_log(LL_FUNC, "purgeArea() end");
674         return;
675     }
676 
677     /* generated tmp-FileName */
678 #ifdef __DOS__
679     xstrscat(&newName, "_sqpktmp", NULL);
680 #else
681     xstrscat(&newName, oldName, "_tmp", NULL);
682 #endif
683 
684     oldArea = MsgOpenArea((byte *) oldName, MSGAREA_NORMAL, (word) areaType);
685 
686     if (oldArea) {
687         if (areaType == MSGTYPE_SDM)
688             newArea = oldArea;
689         else
690             newArea = MsgOpenArea((byte *) newName, MSGAREA_CREATE, (word) areaType);
691     }
692 
693     if ((oldArea != NULL) && (newArea != NULL)) {
694         ULONG lcount;
695 
696         MsgLock(oldArea);
697         highMsg = MsgGetHighMsg(oldArea);
698         numMsg = MsgGetNumMsg(oldArea);
699         if (areaType != MSGTYPE_SDM) hw = MsgGetHighWater(oldArea);
700         readLastreadFile(oldName, &oldLastread, &lcount, oldArea, areaType);
701         if (oldLastread) {
702             newLastread = (UINT32 *) malloc(lcount * sizeof(UINT32));
703             memcpy(newLastread, oldLastread, lcount * sizeof(UINT32));
704         }
705 
706         removeMap = (UINT32 *) calloc(2, sizeof(UINT32));
707 
708         for (i = j = 1; i <= numMsg; i++, j++) {
709             if (!processMsg(j, numMsg, oldArea, newArea, area,
710                 removeMap[0]==1 ? removeMap[1] : 0)) {
711                 if (!(rmIndex & 1)) {
712                     /* We started to delete new portion of */
713                     removeMap = (UINT32 *) realloc(removeMap, (rmIndex + 2) * sizeof(UINT32));
714                     removeMap[rmIndex++] = i;
715                     removeMap[rmIndex] = 0;
716                 };
717                 removeMap[rmIndex]++; /* Anyway, update counter */
718                 if (areaType == MSGTYPE_SDM)
719                     MsgKillMsg(oldArea, j--);
720             } else {
721                 /* We are copying msgs */
722                 if (rmIndex & 1) rmIndex++;
723             };
724         };
725 
726         if (rmIndex > 2 || removeMap[0] > 1) {
727             for (i = 1; i <= numMsg; i++)
728                 updateMsgLinks(i, newArea, rmIndex, removeMap, areaType);
729         }
730 
731         if (areaType == MSGTYPE_SDM) {
732             /* renumber the area */
733             /* TODO: update replylinks */
734             char oldmsgname[PATHLEN], newmsgname[PATHLEN];
735             int  pathlen;
736             numMsg = MsgGetNumMsg(oldArea);
737             strncpy(oldmsgname, oldName, PATHLEN);
738             Add_Trailing(oldmsgname, PATH_DELIM);
739             strncpy(newmsgname, oldmsgname, PATHLEN);
740             pathlen = strlen(oldmsgname);
741             for (i = 1; i <= numMsg; i++) {
742                 j = MsgMsgnToUid(oldArea, i);
743                 if (i == j) continue;
744                 sprintf(oldmsgname+pathlen, "%u.msg", (unsigned int)j);
745                 sprintf(newmsgname+pathlen, "%u.msg", (unsigned int)i);
746                 rename(oldmsgname, newmsgname);
747             }
748         }
749 
750         if (rmIndex) { /* someting was removed, maybe need to update lastreadfile */
751             for (j = 0; j < lcount; j++) {
752                 for (i=0; i<rmIndex; i+=2) {
753                     if (oldLastread[j] >= removeMap[i]) {
754                         if (oldLastread[j] >= removeMap[i] + removeMap[i+1]) {
755                             newLastread[j] -= removeMap[i+1];
756                         } else {
757                             newLastread[j] -= oldLastread[j] - removeMap[i] + 1;
758                         }
759                     }
760                 }
761             }
762         }
763 
764         writeLastreadFile(oldName, newLastread, lcount, newArea, areaType);
765 
766         MsgUnlock(oldArea);
767         MsgCloseArea(oldArea);
768         if (areaType != MSGTYPE_SDM) {
769             if ((numMsg - msgCopied) > hw) hw=0;
770             else hw -= (numMsg - msgCopied);
771             MsgSetHighWater(newArea, hw);
772             MsgCloseArea(newArea);
773         }
774 
775         w_log(  LL_STAT, "      old  msg:%10lu; new  msg:%10lu", (unsigned long)numMsg, msgCopied);
776         totaloldMsg+=numMsg; totalmsgCopied+=msgCopied; /*  total */
777         nfree(oldLastread);
778         nfree(newLastread);
779 
780         /* rename oldArea to newArea */
781         if (renameArea(areaType, oldName, newName))
782             w_log(LL_ERR, "Couldn't rename message base %s to %s: %s!",
783                   oldName, newName, strerror(errno));
784     }
785     else {
786         if (oldArea) {
787             MsgCloseArea(oldArea);
788             if (areaType & MSGTYPE_SDM )
789               w_log(LL_ERR, "Could not create '%s%c*.msg'!", newName, PATH_DELIM );
790             else
791               w_log(LL_ERR, "Could not create '%s.*'!", newName );
792         }else{
793             if (areaType & MSGTYPE_SDM )
794               w_log(LL_ERR, "Could not open '%s%c*.msg'!", oldName, PATH_DELIM );
795             else
796               w_log(LL_ERR, "Could not open '%s.*'!", oldName );
797         }
798     }
799     nfree(newName);
800     w_log(LL_FUNC, "purgeArea() end");
801 }
802 
handleArea(s_area * area)803 void handleArea(s_area *area)
804 {
805     ULONG freeSpace = 0;
806     int process = 1;
807 
808     area_found = 1;
809     w_log(LL_FUNC, "handleArea() begin");
810     if ((area->msgbType & MSGTYPE_SQUISH) == MSGTYPE_SQUISH ||
811         (area->msgbType & MSGTYPE_JAM) == MSGTYPE_JAM ||
812         (area->msgbType & MSGTYPE_SDM) == MSGTYPE_SDM)
813     {
814         struct stat sb;
815         ULONG baseSize = 0;
816         char *msgBaseDir = sstrdup(area->fileName);
817         char *p          = strrchr(msgBaseDir, PATH_DELIM);
818 
819         if(p) *p = '\0';
820         freeSpace = husky_GetDiskFreeSpace(msgBaseDir);
821         if(p) *p = PATH_DELIM;
822 
823         if ((area->msgbType & MSGTYPE_SQUISH) == MSGTYPE_SQUISH)
824         {
825             xstrcat(&msgBaseDir, ".sqd");
826             memset(&sb,0,sizeof(sb));
827             stat(msgBaseDir,&sb);
828             baseSize += sb.st_size;
829             msgBaseDir[strlen(msgBaseDir)-1] = 'i';
830             memset(&sb,0,sizeof(sb));
831             stat(msgBaseDir,&sb);
832             baseSize += sb.st_size;
833         }
834         if ((area->msgbType & MSGTYPE_JAM) == MSGTYPE_JAM)
835         {
836             xstrcat(&msgBaseDir, ".jdt");
837             memset(&sb,0,sizeof(sb));
838             stat(msgBaseDir,&sb);
839             baseSize += sb.st_size;
840             msgBaseDir[strlen(msgBaseDir)-1] = 'x';
841             memset(&sb,0,sizeof(sb));
842             stat(msgBaseDir,&sb);
843             baseSize += sb.st_size;
844             msgBaseDir[strlen(msgBaseDir)-2] = 'h';
845             msgBaseDir[strlen(msgBaseDir)-1] = 'r';
846             memset(&sb,0,sizeof(sb));
847             stat(msgBaseDir,&sb);
848             baseSize += sb.st_size;
849          }
850         baseSize /= 1024; /* convert to Kbytes */
851         if(baseSize >= freeSpace && config->minDiskFreeSpace != 0)
852             process = 0;
853 
854         if(process)
855         {
856             w_log( LL_INFO, "Purge area %s (%s)", area -> areaName,
857                 area -> msgbType & MSGTYPE_SQUISH ? "squish" :
858             area -> msgbType & MSGTYPE_JAM ?    "jam" :
859             area -> msgbType & MSGTYPE_SDM ?    "msg/OPUS" : "unknown type"
860                 );
861             msgCopied = 0;
862             msgProcessed = 0;
863             purgeArea(area);
864         }
865         else
866         {
867             w_log( LL_CRIT, "Not enough free space for purge area %s (avaiable %ulK, need %ulK)",
868                             area -> areaName, freeSpace, baseSize);
869         }
870     };
871     w_log(LL_FUNC, "handleArea() end");
872 }
873 
doArea(s_area * area,char ** areaMasks,int areaMaskCount)874 void doArea(s_area *area, char **areaMasks, int areaMaskCount)
875 {
876     int i;
877     int wasInclusion = 0; /* states that there are one or more inclusion masks */
878                           /* and areas that didn't matched the inclusion mask */
879                           /* should not be processed */
880 
881     if (area) {
882 
883         /* check for inclusion */
884         for (i = 0; i < areaMaskCount; i++) {
885             if (*areaMasks[i] == '!') continue;
886             wasInclusion++;
887             if (patimat(area->areaName, areaMasks[i])) break;
888         }
889         if (wasInclusion && (i == areaMaskCount)) return;  /* not in inclusion mask */
890 
891         /* check for exclusion */
892         for (i = 0; i < areaMaskCount; i++) {
893             if (*areaMasks[i] != '!') continue;
894             if (patimat(area->areaName, areaMasks[i]+1)) break;
895         }
896         if (i != areaMaskCount) return;  /* is in exclusion mask */
897 
898         handleArea(area);
899     }
900 }
901 
main(int argc,char ** argv)902 int main(int argc, char **argv) {
903 
904     unsigned int i;
905     struct _minf m;
906     char *configFile = NULL;
907 
908     int areaMaskCount = 0;
909     char **areaMasks = NULL;
910 
911 #if defined ( __NT__ )
912     SetUnhandledExceptionFilter(&UExceptionFilter);
913 #endif
914 
915     area_found = 0;
916 
917     versionStr = GenVersionStr( "sqpack", VER_MAJOR, VER_MINOR, VER_PATCH,
918                                VER_BRANCH, cvs_date );
919     printf("%s\n", versionStr);
920 
921     if (argc <= 1) {
922         printf ("sqpack purges messages from squish or jam message bases\n");
923         printf ("according to -p and -m parameters in EchoArea lines\n");
924         printf ("Usage: sqpack [-c config] <[!]areamask> [ [!]areamask ... ]\n");
925         return 0;
926     }
927 
928     areaMasks = scalloc(sizeof(char *), argc);
929 
930     i = 0;
931     while (i < (unsigned)argc-1) {
932         i++;
933         if (stricmp(argv[i], "-c") == 0) {
934             if (i < (unsigned)argc-1) {
935                 i++;
936                 configFile = argv[i];
937             } else {
938                 printf("Error: config filename missing on command line\n");
939                 return 1;
940             }
941         } else {
942             areaMasks[areaMaskCount] = argv[i];
943             areaMaskCount++;
944         }
945     }
946 
947     if (!areaMaskCount) {
948         printf("Error: at least one area mask should be specified\n");
949         return 1;
950     }
951 
952     setvar("module", "sqpack");
953     config = readConfig(configFile);  /* if config file not specified on command */
954                                       /* line, NULL would be passed. That's ok. */
955 
956     if (!config) {
957         printf("Error: can't read fido config\n");
958         return 1;
959     }
960 
961     if (config->lockfile) {
962         lock_fd = lockFile(config->lockfile, config->advisoryLock);
963         if( lock_fd < 0 )
964         {
965             disposeConfig(config);
966             exit(EX_CANTCREAT);
967         }
968     }
969     initLog(config->logFileDir, config->logEchoToScreen, config->loglevels, config->screenloglevels);
970     openLog(LOGFILE, versionStr);
971     w_log(LL_START, "Start");
972     m.req_version = 0;
973     m.def_zone = (word)config->addr[0].zone;
974     if (MsgOpenApi(&m)!= 0) {
975         w_log(LL_CRIT,"MsgOpenApi Error. Exit.");
976         closeLog();
977         if (config->lockfile) {
978             FreelockFile(config->lockfile ,lock_fd);
979         }
980         disposeConfig(config);
981         exit(1);
982     }
983 
984     /* purge dupe area */
985     if(config->dupeArea.areaName && config->dupeArea.fileName)
986        doArea(&(config->dupeArea), areaMasks, areaMaskCount);
987     /* purge bad area  */
988     if(config->badArea.areaName && config->badArea.fileName)
989        doArea(&(config->badArea), areaMasks, areaMaskCount);
990 
991     for (i=0; i < config->netMailAreaCount; i++)
992         /*  purge netmail areas */
993         doArea(&(config->netMailAreas[i]), areaMasks, areaMaskCount);
994 
995     for (i=0; i < config->echoAreaCount; i++)
996         /*  purge echomail areas */
997         doArea(&(config->echoAreas[i]), areaMasks, areaMaskCount);
998 
999     for (i=0; i < config->localAreaCount; i++)
1000         /*  purge local areas */
1001         doArea(&(config->localAreas[i]), areaMasks, areaMaskCount);
1002 
1003     if (area_found) {
1004         w_log(LL_SUMMARY,"Total old  msg:%10lu; new  msg:%10lu",
1005               (unsigned long)totaloldMsg, (unsigned long)totalmsgCopied);
1006         w_log(LL_SUMMARY,"Total old size:%10lu; new size:%10lu",
1007               (unsigned long)totalOldBaseSize, (unsigned long)totalNewBaseSize);
1008     } else {
1009         w_log(LL_WARN, "No areas found");
1010     }
1011     w_log(LL_STOP,"End");
1012     closeLog();
1013     if (config->lockfile) {
1014         FreelockFile(config->lockfile ,lock_fd);
1015     }
1016     disposeConfig(config);
1017     return 0;
1018 }
1019