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