1 //  This may look like C code, but it is really -*- C++ -*-
2 
3 //  ------------------------------------------------------------------
4 //  The Goldware Library
5 //  Copyright (C) 2000 Alexander S. Aganichev
6 //  ------------------------------------------------------------------
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License as
9 //  published by the Free Software Foundation; either version 2 of the
10 //  License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 //  General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //  ------------------------------------------------------------------
21 //  $Id: gmosmb1.cpp,v 1.22 2016/02/01 14:29:17 vasilyevmax Exp $
22 //  ------------------------------------------------------------------
23 //  Synchronet message base
24 //  ------------------------------------------------------------------
25 
26 #include <cerrno>
27 #include <gcrcall.h>
28 #include <gstrall.h>
29 #include <gmemall.h>
30 #include <gmemdbg.h>
31 #include <gdbgerr.h>
32 #include <gdbgtrk.h>
33 #include <gmosmb.h>
34 
35 
36 //  ------------------------------------------------------------------
37 
38 smb_t* smbdata = NULL;
39 int smbdatano = 0;
40 
41 
42 //  ------------------------------------------------------------------
43 
SMBExit()44 void SMBExit() {
45 
46   throw_release(smbdata);
47 }
48 
49 
50 //  ------------------------------------------------------------------
51 
SMBInit()52 void SMBInit() {
53 
54   smbdata = (smb_t *)throw_calloc(3, sizeof(smb_t));
55 }
56 
57 
58 //  ------------------------------------------------------------------
59 
data_open()60 void SMBArea::data_open() {
61 
62   data = smbdata + (smbdatano++);
63   strxcpy(data->file, real_path(), sizeof(data->file) - 3);
64   data->sdt_fp = data->shd_fp = data->sid_fp = data->sda_fp = data->sha_fp = NULL;
65   data->retry_time = 1;
66   data->last_error[0] = NUL;
67 }
68 
69 
70 //  ------------------------------------------------------------------
71 
data_close()72 void SMBArea::data_close() {
73 
74   smbdatano--;
75   data = NULL;
76 }
77 
78 
79 //  ------------------------------------------------------------------
80 //  Open the Synchronet message base
81 
open()82 void SMBArea::open() {
83 
84   GFTRK("SMBOpen");
85 
86   isopen++;
87   if(isopen > 2) {
88     WideLog->ErrTest();
89     WideLog->printf("! Trying to open a Synchronet msgbase more than twice.");
90     WideLog->printf(": %s, %s.", echoid(), path());
91     WideLog->printf("+ Info: This indicates a serious bug.");
92     WideLog->printf("+ Advice: Report to the Author immediately.");
93     TestErrorExit();
94   }
95   if(isopen == 1) {
96     if(ispacked()) {
97       isopen--;
98       const char* newpath = Unpack(path());
99       if(newpath == NULL)
100         packed(false);
101       set_real_path(newpath ? newpath : path());
102       isopen++;
103     }
104     data_open();
105 
106     int _tries = 0;
107 
108     for(;;) {
109       if(smb_open(data) != 0) {
110 
111         if((errno != EACCES) or (PopupLocked(++_tries, false, data->file) == false)) {
112 
113           // User requested to exit
114           WideLog->ErrOpen();
115           WideLog->printf("! Synchronet message base could not be opened (%s).", data->last_error);
116           WideLog->printf(": %s", real_path());
117           WideLog->ErrOSInfo();
118           OpenErrorExit();
119         }
120       }
121       else
122         break;
123     };
124 
125     // Remove the popup window
126     if(_tries)
127       PopupLocked(0, 0, NULL);
128 
129     if(not fsize(data->shd_fp)) {
130       data->status.max_crcs = 0;
131       data->status.max_age = 0;
132       data->status.max_msgs = 1000;
133       data->status.attr = 0;
134       if(smb_create(data) != 0) {
135         smb_close(data);
136 
137         WideLog->ErrOpen();
138         WideLog->printf("! Synchronet message base could not be created (%s).", data->last_error);
139         WideLog->printf(": %s", real_path());
140         WideLog->ErrOSInfo();
141         OpenErrorExit();
142       }
143     }
144     scan();
145   }
146 
147   GFTRK(0);
148 }
149 
150 
151 //  ------------------------------------------------------------------
152 
close()153 void SMBArea::close()
154 {
155   GFTRK("SMBClose");
156 
157   if(isopen) {
158     if(isopen == 1) {
159       smb_close(data);
160       data_close();
161       if(ispacked()) {
162         CleanUnpacked(real_path());
163       }
164     }
165     isopen--;
166   }
167   else {
168     WideLog->ErrTest();
169     WideLog->printf("! Trying to close an already closed Synchronet msgbase.");
170     WideLog->printf(": %s, %s.", echoid(), path());
171     WideLog->printf("+ Info: This indicates a potentially serious bug.");
172     WideLog->printf("+ Advice: Report to the Author immediately.");
173     TestErrorExit();
174   }
175 
176   GFTRK(0);
177 }
178 
179 
180 //  ------------------------------------------------------------------
181 
suspend()182 void SMBArea::suspend()
183 {
184   smb_close(data);
185 }
186 
187 
188 //  ------------------------------------------------------------------
189 
resume()190 void SMBArea::resume()
191 {
192   int _tries = 0;
193 
194   for(;;) {
195     if(smb_open(data) != 0) {
196 
197       if((errno != EACCES) or (PopupLocked(++_tries, false, data->file) == false)) {
198 
199         // User requested to exit
200         WideLog->ErrOpen();
201         WideLog->printf("! Synchronet message base could not be opened (%s).", data->last_error);
202         WideLog->printf(": %s", real_path());
203         WideLog->ErrOSInfo();
204         OpenErrorExit();
205       }
206     }
207     else
208       break;
209   };
210 
211   // Remove the popup window
212   if(_tries)
213     PopupLocked(0, 0, NULL);
214 
215   if(not fsize(data->shd_fp)) {
216     data->status.max_crcs = 0;
217     data->status.max_age = 0;
218     data->status.max_msgs = 1000;
219     data->status.attr = 0;
220     if(smb_create(data) != 0) {
221       smb_close(data);
222 
223       WideLog->ErrOpen();
224       WideLog->printf("! Synchronet message base could not be created (%s).", data->last_error);
225       WideLog->printf(": %s", real_path());
226       WideLog->ErrOSInfo();
227       OpenErrorExit();
228     }
229   }
230 }
231 
232 
233 //  ------------------------------------------------------------------
234 
lock()235 void SMBArea::lock()
236 {
237 }
238 
239 
240 //  ------------------------------------------------------------------
241 
unlock()242 void SMBArea::unlock()
243 {
244 }
245 
246 
247 //  ------------------------------------------------------------------
248 
load_hdr(gmsg * __msg,smbmsg_t * smsg)249 int SMBArea::load_hdr(gmsg* __msg, smbmsg_t *smsg)
250 {
251   GFTRK("SMBLoadHdr");
252 
253   smbmsg_t local_smsg, *smsgp;
254   smsgp = smsg ? smsg : &local_smsg;
255   uint32_t reln = Msgn->ToReln(__msg->msgno);
256   if(reln == 0) {
257     GFTRK(0);
258     return false;
259   }
260   fseek(data->sid_fp, (reln - 1L) * sizeof(idxrec_t), SEEK_SET);
261   if(not fread(&smsgp->idx, 1, sizeof(idxrec_t), data->sid_fp) or (smb_lockmsghdr(data, smsgp) != 0)) {
262     GFTRK(0);
263     return false;
264   }
265   int rv = smb_getmsghdr(data, smsgp);
266   smb_unlockmsghdr(data, smsgp);
267   if(rv != 0) {
268     GFTRK(0);
269     return false;
270   }
271   __msg->link.to_set(smsgp->hdr.thread_orig);
272   __msg->link.next_set(smsgp->hdr.thread_next);
273   __msg->link.first_set(smsgp->hdr.thread_first);
274 
275   strxcpy(__msg->by, (char *)smsgp->from, 36);
276   strxcpy(__msg->to, (char *)smsgp->to, 36);
277   strxcpy(__msg->re, (char *)smsgp->subj, 72);
278 
279   if(smsgp->from_net.type == NET_FIDO) {
280     __msg->orig.zone  = __msg->oorig.zone  = ((fidoaddr_t *)smsgp->from_net.addr)->zone;
281     __msg->orig.net   = __msg->oorig.net   = ((fidoaddr_t *)smsgp->from_net.addr)->net;
282     __msg->orig.node  = __msg->oorig.node  = ((fidoaddr_t *)smsgp->from_net.addr)->node;
283     __msg->orig.point = __msg->oorig.point = ((fidoaddr_t *)smsgp->from_net.addr)->point;
284   }
285   else {
286     __msg->orig.zone  = __msg->oorig.zone  = 0;
287     __msg->orig.net   = __msg->oorig.net   = 0;
288     __msg->orig.node  = __msg->oorig.node  = 0;
289     __msg->orig.point = __msg->oorig.point = 0;
290   }
291   if(smsgp->to_net.type == NET_FIDO) {
292     __msg->dest.zone  = __msg->odest.zone  = ((fidoaddr_t *)smsgp->to_net.addr)->zone;
293     __msg->dest.net   = __msg->odest.net   = ((fidoaddr_t *)smsgp->to_net.addr)->net;
294     __msg->dest.node  = __msg->odest.node  = ((fidoaddr_t *)smsgp->to_net.addr)->node;
295     __msg->dest.point = __msg->odest.point = ((fidoaddr_t *)smsgp->to_net.addr)->point;
296   }
297   else {
298     __msg->dest.zone  = __msg->odest.zone  = 0;
299     __msg->dest.net   = __msg->odest.net   = 0;
300     __msg->dest.node  = __msg->odest.node  = 0;
301     __msg->dest.point = __msg->odest.point = 0;
302   }
303 
304   // Transfer attributes
305   __msg->attr.pvt(smsgp->hdr.attr & MSG_PRIVATE);
306   __msg->attr.rcv(smsgp->hdr.attr & MSG_READ);
307   __msg->attr.lok(smsgp->hdr.attr & MSG_LOCKED);
308   __msg->attr.del(smsgp->hdr.attr & MSG_DELETE);
309   __msg->attr.r_o(smsgp->hdr.attr & MSG_PERMANENT);
310   __msg->attr.loc(smsgp->hdr.netattr & MSG_LOCAL);
311   __msg->attr.cra(smsgp->hdr.netattr & MSG_CRASH);
312   __msg->attr.imm(smsgp->hdr.netattr & MSG_IMMEDIATE);
313   __msg->attr.dir(smsgp->hdr.netattr & MSG_DIRECT);
314   __msg->attr.hld(smsgp->hdr.netattr & MSG_HOLD);
315   __msg->attr.snt(smsgp->hdr.netattr & MSG_SENT);
316   __msg->attr.trs(smsgp->hdr.netattr & MSG_INTRANSIT);
317   __msg->attr.orp(smsgp->hdr.netattr & MSG_ORPHAN);
318   __msg->attr.k_s(smsgp->hdr.netattr & MSG_KILLSENT);
319   __msg->attr.a_s(smsgp->hdr.netattr & MSG_ARCHIVESENT);
320   __msg->attr.zon(smsgp->hdr.netattr & MSG_GATE);
321   __msg->attr.att(smsgp->hdr.auxattr & MSG_FILEATTACH);
322   __msg->attr.frq(smsgp->hdr.auxattr & MSG_FILEREQUEST);
323   __msg->attr.rrq(smsgp->hdr.auxattr & MSG_RECEIPTREQ);
324   __msg->attr.cfm(smsgp->hdr.auxattr & MSG_CONFIRMREQ);
325   __msg->attr.tfs(smsgp->hdr.auxattr & MSG_TRUNCFILE);
326 
327   time32_t a  = smsgp->hdr.when_written.time;
328   struct tm tp; ggmtime(&tp, &a);
329   tp.tm_isdst = -1;
330   time32_t b  = gmktime(&tp);
331   __msg->written = a + a - b;
332   a = smsgp->hdr.when_imported.time;
333   ggmtime(&tp, &a);
334   b = gmktime(&tp);
335   __msg->arrived = a + a - b;
336   __msg->received = 0;
337 
338   if(not smsg)
339     smb_freemsgmem(smsgp);
340   GFTRK(0);
341   return true;
342 }
343 
344 
345 //  ------------------------------------------------------------------
346 
load_msg(gmsg * msg)347 int SMBArea::load_msg(gmsg* msg)
348 {
349   smbmsg_t smsg;
350   uint16_t xlat;
351   uint8_t  *inbuf;
352   int32_t outlen;
353   char buf[512];
354   int i;
355   bool lzh;
356   bool tail = true;
357   uint32_t l;
358   uint32_t txt_len = 1;
359 
360   GFTRK("SMBLoadMsg");
361 
362   if(not load_hdr(msg, &smsg)) {
363     GFTRK(0);
364     return false;
365   }
366 
367   msg->txt = throw_strdup("");
368 
369   if(smsg.from_net.type == NET_INTERNET) {
370     sprintf(buf, "\001From: %s (%s)\r", (char *)smsg.from_net.addr, smsg.from);
371     outlen = strlen(buf);
372     msg->txt = (char *)throw_realloc(msg->txt, txt_len+outlen);
373     strcpy(msg->txt+txt_len-1, buf);
374     txt_len+=outlen;
375   }
376   if(smsg.to_net.type == NET_INTERNET) {
377     sprintf(buf, "\001To: %s (%s)\r", (char *)smsg.to_net.addr, smsg.to);
378     outlen = strlen(buf);
379     msg->txt = (char *)throw_realloc(msg->txt, txt_len+outlen);
380     strcpy(msg->txt+txt_len-1, buf);
381     txt_len+=outlen;
382   }
383 
384   for(i = 0; i < smsg.total_hfields; i++)
385     switch(smsg.hfield[i].type) {
386       case RFC822MSGID:
387         sprintf(buf, "\001Message-ID: %s\r", (char *)smsg.hfield_dat[i]);
388         goto add;
389       case FIDOMSGID:
390         sprintf(buf, "\001MSGID: %s\r", (char *)smsg.hfield_dat[i]);
391         goto add;
392       case RFC822REPLYID:
393         sprintf(buf, "\001In-Reply-To: %s\r", (char *)smsg.hfield_dat[i]);
394         goto add;
395       case FIDOREPLYID:
396         sprintf(buf, "\001REPLY: %s\r", (char *)smsg.hfield_dat[i]);
397         goto add;
398       case FIDOPID:
399         sprintf(buf, "\001PID: %s\r", (char *)smsg.hfield_dat[i]);
400         strcpy(msg->pid, (char *)smsg.hfield_dat[i]);
401         goto add;
402       case FIDOAREA:
403         sprintf(buf, "\001AREA: %s\r", (char *)smsg.hfield_dat[i]);
404         goto add;
405       case FIDOFLAGS:
406         sprintf(buf, "\001FLAGS: %s\r", (char *)smsg.hfield_dat[i]);
407         goto add;
408       case FIDOCTRL:
409         if(not strncmp((char *)smsg.hfield_dat[i], "Via ", 4) or not strncmp((char *)smsg.hfield_dat[i], "Recd ", 5))
410           break;
411       case RFC822HEADER:
412         if(not strncmp((char *)smsg.hfield_dat[i], "Via:", 4))
413           break;
414         sprintf(buf, "\001%s\r", (char *)smsg.hfield_dat[i]);
415 add:
416         outlen = strlen(buf);
417         msg->txt = (char *)throw_realloc(msg->txt, txt_len+outlen);
418         strcpy(msg->txt+txt_len-1, buf);
419         txt_len+=outlen;
420         break;
421       default:
422         break;
423     }
424 
425   for(i = 0; i < smsg.hdr.total_dfields; i++)
426     switch(smsg.dfield[i].type) {
427       case TEXT_BODY:
428         if(tail)
429           tail = false;
430         goto common;
431       case TEXT_TAIL:
432         if(not tail) {
433           tail = true;
434           msg->txt[txt_len-1] = '\r';
435           txt_len++;
436         }
437 common:
438         fseek(data->sdt_fp, smsg.hdr.offset + smsg.dfield[i].offset, SEEK_SET);
439         l = sizeof(xlat);
440         fread(&xlat, sizeof(xlat), 1, data->sdt_fp);
441         lzh = false;
442         if(xlat == XLAT_LZH) {
443           lzh = true;
444           fread(&xlat, sizeof(xlat), 1, data->sdt_fp);
445           l += sizeof(xlat);
446         }
447         if(xlat != XLAT_NONE) /* no other translations currently supported */
448           continue;
449         if(lzh) {
450           inbuf = (uint8_t *)throw_xmalloc(smsg.dfield[i].length);
451           fread(inbuf, smsg.dfield[i].length - l, 1, data->sdt_fp);
452           outlen = *(int32_t *)inbuf;
453           msg->txt = (char *)throw_realloc(msg->txt, txt_len+outlen);
454           lzh_decode(inbuf, smsg.dfield[i].length - l, (uint8_t *)(msg->txt+txt_len-1));
455           throw_xfree(inbuf);
456         }
457         else if(l < smsg.dfield[i].length) {
458           outlen = smsg.dfield[i].length - l;
459           msg->txt = (char *)throw_realloc(msg->txt, txt_len+outlen);
460           fread(msg->txt+txt_len-1, smsg.dfield[i].length - l, 1, data->sdt_fp);
461         }
462         else
463           outlen = 0;
464         txt_len += outlen;
465         msg->txt[txt_len-1] = NUL;
466         break;
467     }
468 
469   for(i = 0; i < smsg.total_hfields; i++)
470     switch(smsg.hfield[i].type) {
471       case FIDOPATH:
472         sprintf(buf, "\r\001PATH: %s", (char *)smsg.hfield_dat[i]);
473         goto add2;
474       case FIDOSEENBY:
475         sprintf(buf, "\rSEEN-BY: %s", (char *)smsg.hfield_dat[i]);
476         goto add2;
477       case FIDOCTRL:
478         if(strncmp((char *)smsg.hfield_dat[i], "Via ", 4) and strncmp((char *)smsg.hfield_dat[i], "Recd ", 5))
479           break;
480         sprintf(buf, "\r\001%s", (char *)smsg.hfield_dat[i]);
481 add2:
482         outlen = strlen(buf);
483         msg->txt = (char *)throw_realloc(msg->txt, txt_len+outlen);
484         strcpy(msg->txt+txt_len-1, buf);
485         txt_len+=outlen;
486         break;
487       default:
488         break;
489     }
490 
491 
492   smb_freemsgmem(&smsg);
493 
494   GFTRK(0);
495   return true;
496 }
497 
498 //  ------------------------------------------------------------------
499 //  Returns current timezone offset based on TZ environment variable.
500 
tzoffset_in_minutes()501 int tzoffset_in_minutes()
502 {
503   time32_t t1 = gtime(NULL);
504   struct tm tp; ggmtime(&tp, &t1);
505   tp.tm_isdst = -1;
506   time32_t t2 = gmktime(&tp);
507   int dt = (int)(t1 - t2);
508   return dt / 60;
509 }
510 
511 //  ------------------------------------------------------------------
512 
save_hdr(int mode,gmsg * msg)513 void SMBArea::save_hdr(int mode, gmsg* msg)
514 {
515   int rv;
516   char ch;
517   bool done, cr;
518   uint32_t l, m, bodylen, taillen, crc;
519   char *fbuf, *sbody, *stail;
520   char buf[256];
521   smbmsg_t smsg;
522   fidoaddr_t destaddr;
523 
524   GFTRK("SMBSaveHdr");
525 
526   smb_getstatus(data);
527   memset(&smsg, 0, sizeof(smbmsg_t));
528   if(not (mode & GMSG_NEW)) {
529     uint32_t reln = Msgn->ToReln(msg->msgno);
530     if(reln == 0) {
531       GFTRK(0);
532       return;
533     }
534     fseek(data->sid_fp, (reln - 1L) * sizeof(idxrec_t), SEEK_SET);
535     if(not fread(&smsg.idx, 1, sizeof(idxrec_t), data->sid_fp) or (smb_lockmsghdr(data, &smsg) != 0)) {
536       GFTRK(0);
537       return;
538     }
539     int rv = smb_getmsghdr(data, &smsg);
540     smb_unlockmsghdr(data, &smsg);
541     if(rv != 0) {
542       GFTRK(0);
543       return;
544     }
545     smsg.hdr.attr = 0;
546     smsg.hdr.netattr = 0;
547     smsg.hdr.auxattr = 0;
548   }
549   else
550   {
551     memcpy(smsg.hdr.id, "SHD\x1a", 4);
552     smsg.hdr.version = smb_ver();
553     struct tm tp; ggmtime(&tp, &msg->written);
554     tp.tm_isdst = -1;
555     smsg.hdr.when_written.time = gmktime(&tp);
556     smsg.hdr.when_written.zone = tzoffset_in_minutes();
557   }
558   smsg.hdr.when_imported.time = gtime(NULL);
559   smsg.hdr.when_imported.zone = tzoffset_in_minutes();
560 
561   // Transfer attributes
562   if(msg->attr.pvt()) smsg.hdr.attr |= MSG_PRIVATE;
563   if(msg->attr.rcv()) smsg.hdr.attr |= MSG_READ;
564   if(msg->attr.lok()) smsg.hdr.attr |= MSG_LOCKED;
565   if(msg->attr.del()) smsg.hdr.attr |= MSG_DELETE;
566   if(msg->attr.r_o()) smsg.hdr.attr |= MSG_PERMANENT;
567   if(msg->attr.loc()) smsg.hdr.netattr |= MSG_LOCAL;
568   if(msg->attr.cra()) smsg.hdr.netattr |= MSG_CRASH;
569   if(msg->attr.imm()) smsg.hdr.netattr |= MSG_IMMEDIATE;
570   if(msg->attr.dir()) smsg.hdr.netattr |= MSG_DIRECT;
571   if(msg->attr.hld()) smsg.hdr.netattr |= MSG_HOLD;
572   if(msg->attr.snt()) smsg.hdr.netattr |= MSG_SENT;
573   if(msg->attr.trs()) smsg.hdr.netattr |= MSG_INTRANSIT;
574   if(msg->attr.orp()) smsg.hdr.netattr |= MSG_ORPHAN;
575   if(msg->attr.k_s()) smsg.hdr.netattr |= MSG_KILLSENT;
576   if(msg->attr.a_s()) smsg.hdr.netattr |= MSG_ARCHIVESENT;
577   if(msg->attr.zon()) smsg.hdr.netattr |= MSG_GATE;
578   if(msg->attr.att()) smsg.hdr.auxattr |= MSG_FILEATTACH;
579   if(msg->attr.frq()) smsg.hdr.auxattr |= MSG_FILEREQUEST;
580   if(msg->attr.rrq()) smsg.hdr.auxattr |= MSG_RECEIPTREQ;
581   if(msg->attr.cfm()) smsg.hdr.auxattr |= MSG_CONFIRMREQ;
582   if(msg->attr.tfs()) smsg.hdr.auxattr |= MSG_TRUNCFILE;
583 
584   smsg.hdr.thread_orig = msg->link.to();
585   smsg.hdr.thread_next = msg->link.next();
586   smsg.hdr.thread_first = msg->link.first();
587 
588   if((mode & GMSG_UPDATE) and not (mode & GMSG_TXT)) {
589     if(mode & GMSG_NEW)
590       smb_addmsghdr(data, &smsg, data->status.attr & SMB_HYPERALLOC);
591     else {
592       smsg.idx.time = smsg.hdr.when_imported.time;
593       smsg.idx.attr = smsg.hdr.attr;
594       smsg.offset = Msgn->ToReln(msg->msgno) - 1;
595       smb_putmsg(data, &smsg);
596     }
597     smb_freemsgmem(&smsg);
598     GFTRK(0);
599     return;
600   }
601 
602   if(not (mode & GMSG_NEW)) {
603     if(not (data->status.attr & SMB_HYPERALLOC)) {
604       if(smb_open_da(data) == 0) {
605         if(smb_open_ha(data) == 0) {
606           smb_freemsg(data, &smsg);
607           smb_close_ha(data);
608         }
609         smb_close_da(data);
610       }
611     }
612     else {
613       smb_freemsg(data, &smsg);
614     }
615     smb_freemsgmem(&smsg);
616     smsg.dfield = NULL;
617     smsg.hdr.total_dfields = 0;
618     smsg.total_hfields = 0;
619     smsg.hfield = NULL;
620     smsg.hfield_dat = NULL;
621   }
622 
623   uint16_t net = NET_FIDO;
624   destaddr.zone = msg->dest.zone;
625   destaddr.net = msg->dest.net;
626   destaddr.node = msg->dest.node;
627   destaddr.point = msg->dest.point;
628   smb_hfield(&smsg, RECIPIENTNETTYPE, sizeof(uint16_t), &net);
629   smb_hfield(&smsg, RECIPIENTNETADDR, sizeof(fidoaddr_t), &destaddr);
630 
631   smb_hfield(&smsg, SENDER, strlen(msg->by), msg->by);
632   strcpy(buf, msg->by);
633   strlwr(buf);
634   smsg.idx.from = strCrc16c(buf);
635 
636   smb_hfield(&smsg, RECIPIENT, strlen(msg->to), msg->to);
637   strcpy(buf, msg->to);
638   strlwr(buf);
639   smsg.idx.to = strCrc16c(buf);
640 
641   smb_hfield(&smsg, SUBJECT, strlen(msg->re), msg->re);
642   strcpy(buf, msg->re);
643   strlwr(buf);
644   smsg.idx.subj = strCrc16c(buf);
645 
646   // calculate maximum possible size of sbody/stail
647   for(l = 0, fbuf = msg->txt; *fbuf != NUL; l++, fbuf++)
648     if(*fbuf == CR)
649       ++l;
650 
651   l += 2; // reserve 2 bytes for the encoding type
652 
653   fbuf = msg->txt;
654   sbody = (char *)throw_malloc(SDT_BLOCK_LEN*smb_datblocks(l))+2;
655   stail = (char *)throw_malloc(SDT_BLOCK_LEN*smb_datblocks(l))+2;
656 
657   for(l = bodylen = taillen = done = 0, cr = true; (ch = fbuf[l]) != NUL; l++) {
658     if(ch == CTRL_A and cr) {
659       ++l;
660       if(not strncmp(fbuf+l, "MSGID:", 6)) {
661         l += 6;
662         while(fbuf[l] and fbuf[l] == ' ') l++;
663         m = l;
664         while(fbuf[m] and not iscntrl(fbuf[m])) m++;
665         if(m != l)
666           smb_hfield(&smsg, FIDOMSGID, m-l, fbuf+l);
667       }
668       else if(not strncmp(fbuf+l, "REPLY:", 6)) {
669         l += 6;
670         while(fbuf[l] and fbuf[l] == ' ') l++;
671         m = l;
672         while(fbuf[m] and not iscntrl(fbuf[m])) m++;
673         if(m != l)
674           smb_hfield(&smsg, FIDOREPLYID, m-l, fbuf+l);
675       }
676       else if(not strncmp(fbuf+l, "FLAGS:", 6)) {
677         l += 6;
678         while(fbuf[l] and fbuf[l] == ' ') l++;
679         m = l;
680         while(fbuf[m] and not iscntrl(fbuf[m])) m++;
681         if(m != l)
682           smb_hfield(&smsg, FIDOFLAGS, m-l, fbuf+l);
683       }
684       else if(not strncmp(fbuf+l, "PATH:", 5)) {
685         l += 5;
686         while(fbuf[l] and fbuf[l] == ' ') l++;
687         m = l;
688         while(fbuf[m] and not iscntrl(fbuf[m])) m++;
689         if(m != l)
690           smb_hfield(&smsg, FIDOPATH, m-l, fbuf+l);
691       }
692       else if(not strncmp(fbuf+l, "PID:", 4)) {
693         l += 4;
694         while(fbuf[l] and fbuf[l] == ' ') l++;
695         m = l;
696         while(fbuf[m] and not iscntrl(fbuf[m])) m++;
697         if(m != l)
698           smb_hfield(&smsg, FIDOPID, m-l, fbuf+l);
699       }
700       else {
701         if(strncmp(fbuf+l, "TOPT ", 5) and strncmp(fbuf+l, "FMPT ", 5) and
702            strncmp(fbuf+l, "INTL ", 5)) {
703           while(fbuf[l] and fbuf[l] == ' ') l++;
704           m = l;
705           while(fbuf[m] and not iscntrl(fbuf[m])) m++;
706           if(m != l)
707             smb_hfield(&smsg, FIDOCTRL, m-l, fbuf+l);
708         }
709       }
710       while(fbuf[l] and fbuf[l]!=CR) l++;
711       continue;
712     }
713     if(ch != LF and ch != SOFTCR) {
714       if(cr) {
715         if(not done and not strncmp(fbuf+l, "--- ", 4) or not strncmp(fbuf+l, "---\r", 4))
716           done = true; // tear line and down go into tail
717         else if(done and not strncmp(fbuf+l, "SEEN-BY:", 8)) {
718           l += 8;
719           while(fbuf[l] and fbuf[l] == ' ') l++;
720           m = l;
721           while(fbuf[m] and not iscntrl(fbuf[m])) m++;
722           if(m != l)
723             smb_hfield(&smsg, FIDOSEENBY, m-l, fbuf+l);
724           while(fbuf[l] and fbuf[l]!=CR) l++;
725           continue;
726         }
727         else if(not done and not strncmp(fbuf+l, " * Origin: ", 11))
728           done = true;
729       }
730       if(done)
731         stail[taillen++] = ch;
732       else
733         sbody[bodylen++] = ch;
734       if(ch == CR) {
735         cr = true;
736         if(done)
737           stail[taillen++] = LF;
738         else
739           sbody[bodylen++] = LF;
740       }
741       else
742         cr = false;
743     }
744   }
745   if(bodylen>=2 && sbody[bodylen-2]==CR && sbody[bodylen-1]==LF)
746     bodylen-=2; // remove last CRLF if present
747 
748   crc = ~memCrc32(sbody, bodylen, false, CRC32_MASK_CCITT);
749   rv = smb_addcrc(data, crc);
750 
751   while(taillen and (iscntrl(stail[taillen-1]) or isspace(stail[taillen-1])))
752     taillen--;
753 
754   l = bodylen+2;
755   if(taillen)
756     l += (taillen+2);
757 
758   if(not (data->status.attr & SMB_HYPERALLOC)) {
759     if(smb_open_da(data) == 0) {
760       smsg.hdr.offset = smb_allocdat(data, l, 1);
761       smb_close_da(data);
762     }
763     else
764       smsg.hdr.offset = (uint32_t)-1L;
765   }
766   else {
767     smsg.hdr.offset = smb_hallocdat(data);
768   }
769 
770   if(smsg.hdr.offset != (uint32_t)-1L) {
771     fseek(data->sdt_fp, smsg.hdr.offset, SEEK_SET);
772     *(uint16_t *)(sbody-2) = XLAT_NONE;
773     l = ftell(data->sdt_fp);
774     fwrite(sbody-2, SDT_BLOCK_LEN, smb_datblocks(bodylen), data->sdt_fp);
775     if(taillen) {
776       fseek(data->sdt_fp, l+bodylen+2, SEEK_SET);
777       *(uint16_t *)(stail-2) = XLAT_NONE;
778       fwrite(stail-2, SDT_BLOCK_LEN, smb_datblocks(taillen), data->sdt_fp);
779     }
780     fflush(data->sdt_fp);
781     smb_dfield(&smsg, TEXT_BODY, bodylen+2);
782     if(taillen)
783       smb_dfield(&smsg, TEXT_TAIL, taillen+2);
784 
785     int storage = data->status.attr & SMB_HYPERALLOC;
786 
787     if(mode & GMSG_NEW) {
788       smb_addmsghdr(data, &smsg, storage);
789       Msgn->Append(smsg.hdr.number);
790     }
791     else {
792       // Changing message... It is always bad idea since it is usually
793       // undescribed and not supported by the API
794       int32_t l;
795 
796       if(data->locked or (smb_locksmbhdr(data) == 0)) {
797         if(smb_getstatus(data) == 0) {
798           if((storage == SMB_HYPERALLOC) or (smb_open_ha(data) == 0)) {
799             smsg.hdr.length = smb_getmsghdrlen(&smsg);
800             if(storage == SMB_HYPERALLOC)
801               l = smb_hallochdr(data);
802             else if(storage == SMB_FASTALLOC)
803               l = smb_fallochdr(data, smsg.hdr.length);
804             else
805               l = smb_allochdr(data, smsg.hdr.length);
806             if(storage != SMB_HYPERALLOC)
807               smb_close_ha(data);
808             if(l!=-1L) {
809               smsg.idx.offset = data->status.header_offset+l;
810               smsg.idx.time = smsg.hdr.when_imported.time;
811               smsg.idx.attr = smsg.hdr.attr;
812               smsg.offset = Msgn->ToReln(msg->msgno) - 1;
813               smb_putmsg(data, &smsg);
814             }
815           }
816         }
817       }
818       smb_unlocksmbhdr(data);
819     }
820   }
821   throw_xfree(sbody-2);
822   throw_xfree(stail-2);
823   smb_freemsgmem(&smsg);
824 
825   GFTRK(0);
826 }
827 
828 
829 //  ------------------------------------------------------------------
830 
save_msg(int mode,gmsg * msg)831 void SMBArea::save_msg(int mode, gmsg* msg)
832 {
833   GFTRK("SMBSaveMsg");
834 
835   save_hdr(mode | GMSG_HDRTXT, msg);
836 
837   GFTRK(0);
838 }
839 
840 
841 //  ------------------------------------------------------------------
842 
del_msg(gmsg * msg)843 void SMBArea::del_msg(gmsg* msg)
844 {
845   GFTRK("SMBDelMsg");
846 
847   smbmsg_t smsg;
848   uint32_t reln = Msgn->ToReln(msg->msgno);
849   if(reln == 0) {
850     GFTRK(0);
851     return;
852   }
853   fseek(data->sid_fp, (reln - 1L) * sizeof(idxrec_t), SEEK_SET);
854   if(not fread(&smsg.idx, 1, sizeof(idxrec_t), data->sid_fp) or (smb_lockmsghdr(data, &smsg) != 0)) {
855     GFTRK(0);
856     return;
857   }
858   if(smb_getmsghdr(data, &smsg) == 0) {
859     smsg.hdr.attr |= MSG_DELETE;
860     smsg.idx.time = smsg.hdr.when_imported.time;
861     smsg.idx.attr = smsg.hdr.attr;
862     smsg.offset = reln - 1L;
863     int rv = smb_putmsg(data, &smsg);
864     smb_unlockmsghdr(data, &smsg);
865     if(rv == 0)
866       msg->attr.del1();
867   }
868   else
869     smb_unlockmsghdr(data, &smsg);
870   smb_freemsgmem(&smsg);
871 
872   GFTRK(0);
873 }
874 
875 
876 //  ------------------------------------------------------------------
877 
new_msgno(gmsg * msg)878 void SMBArea::new_msgno(gmsg* msg)
879 {
880   int res = smb_getstatus(data);
881   smb_unlocksmbhdr(data);
882   msg->msgno = (res == 0) ? data->status.last_msg+1 : 0;
883 }
884 
885 
886 //  ------------------------------------------------------------------
887 
user_lookup(char * lookfor)888 char* SMBArea::user_lookup(char* lookfor)
889 {
890   NW(lookfor);
891   return NULL;
892 }
893 
894 
895 //  ------------------------------------------------------------------
896 
renumber()897 int SMBArea::renumber()
898 {
899   return false;
900 }
901 
902 
903 //  ------------------------------------------------------------------
904 
update_timesread(gmsg * msg)905 void SMBArea::update_timesread(gmsg* msg)
906 {
907   NW(msg);
908   return;
909 }
910 
911 
912 //  ------------------------------------------------------------------
913 
binstr(char * buf,uint16_t length)914 static char *binstr(char *buf, uint16_t length)
915 {
916   static char str[128];
917   char tmp[128];
918   int i;
919 
920   str[0] = 0;
921   for(i = 0; i < length; i++)
922     if(buf[i] and not isprint(buf[i]))
923       break;
924   if(i == length)                /* not binary */
925     return buf;
926 
927   if(length > 42)
928     length = 42;
929 
930   for(i = 0; i < length; i++) {
931     sprintf(tmp, "%02X ", buf[i]);
932     strcat(str, tmp);
933   }
934   return str;
935 }
936 
937 
938 //  ------------------------------------------------------------------
939 
faddrtoa(fidoaddr_t addr)940 static char *faddrtoa(fidoaddr_t addr)
941 {
942   static char str[25];
943   char point[25];
944 
945   sprintf(str, "%u:%u/%u", addr.zone, addr.net, addr.node);
946   if (addr.point) {
947     sprintf(point, ".%u", addr.point);
948     strcat(str, point);
949   }
950   return str;
951 }
952 
953 
954 //  ------------------------------------------------------------------
955 
make_dump_msg(Line * & lin,gmsg * msg,char * lng_head)956 Line* SMBArea::make_dump_msg(Line*& lin, gmsg* msg, char* lng_head)
957 {
958 
959   GFTRK("SMBMakeDump");
960 
961   Line* line = lin =
962   AddLine (NULL, "Hexdump of Synchronet-style message header and text");
963   AddLineF(line, "------------------------------------------------------------------------------");
964   line = AddLine(line, "");
965 
966   smbmsg_t smsg;
967   char buf[512];
968   int i;
969 
970   GFTRK("SMBLoadMsg");
971 
972   if(not load_hdr(msg, &smsg)) {
973     line = AddLine(line, "Error loading header");
974     GFTRK(0);
975     return line;
976   }
977 
978   line = AddLineF(line, "Subject           : %s", smsg.subj);
979   line = AddLineF(line, "From              : %s", smsg.from);
980   line = AddLineF(line, "To                : %s", smsg.to);
981   line = AddLineF(line, "Type              : %04Xh", smsg.hdr.type);
982   line = AddLineF(line, "Version           : %04Xh", smsg.hdr.version);
983   line = AddLineF(line, "Length            : %u", smsg.hdr.length);
984   line = AddLineF(line, "Attr              : %04Xh", smsg.hdr.attr);
985   line = AddLineF(line, "AUXAttr           : %04Xh", smsg.hdr.auxattr);
986   line = AddLineF(line, "NetAttr           : %04Xh", smsg.hdr.netattr);
987   gctime(buf, ARRAYSIZE(buf), &smsg.hdr.when_written.time);
988   line = AddLineF(line, "Written           : %s", buf);
989   gctime(buf, ARRAYSIZE(buf), &smsg.hdr.when_imported.time);
990   line = AddLineF(line, "Imported          : %s", buf);
991   line = AddLineF(line, "Number            : %d (%d)", smsg.hdr.number, (int32_t)(ftell(data->sid_fp)/sizeof(idxrec_t)));
992   line = AddLineF(line, "Thread orig       : %d", smsg.hdr.thread_orig);
993   line = AddLineF(line, "Thread next       : %d", smsg.hdr.thread_next);
994   line = AddLineF(line, "Thread first      : %d", smsg.hdr.thread_first);
995   line = AddLineF(line, "Reserved          : %s", HexDump16(buf, (const char*)smsg.hdr.reserved, 16, "%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X"));
996   line = AddLineF(line, "Offset            : %06Xh", smsg.hdr.offset);
997   line = AddLineF(line, "Total dfields     : %u", smsg.hdr.total_dfields);
998 
999   if(smsg.from_net.type) {
1000     line = AddLineF(line, "From net type     : %02Xh", smsg.from_net.type);
1001     line = AddLineF(line, "From net addr     : %s", (char *)(smsg.from_net.type == NET_FIDO ? faddrtoa(*(fidoaddr_t *)smsg.from_net.addr) : smsg.from_net.addr));
1002   }
1003 
1004   if(smsg.to_net.type) {
1005     line = AddLineF(line, "To net type       : %02Xh", smsg.to_net.type);
1006     line = AddLineF(line, "To net addr       : %s", (char *)(smsg.to_net.type == NET_FIDO ? faddrtoa(*(fidoaddr_t *)smsg.to_net.addr) : smsg.to_net.addr));
1007   }
1008 
1009   if(smsg.replyto_net.type) {
1010     line = AddLineF(line, "Replyto net type  : %02Xh", smsg.replyto_net.type);
1011     line = AddLineF(line, "Replyto net addr  : %s", (char *)(smsg.replyto_net.type == NET_FIDO ? faddrtoa(*(fidoaddr_t *)smsg.replyto_net.addr) : smsg.replyto_net.addr));
1012   }
1013 
1014   line = AddLineF(line, "From agent        : %02Xh", smsg.from_agent);
1015   line = AddLineF(line, "To agent          : %02Xh", smsg.to_agent);
1016   line = AddLineF(line, "Replyto agent     : %02Xh", smsg.replyto_agent);
1017 
1018   line = AddLine(line, "");
1019   AddLineF(line, "dfields:");
1020   line = AddLine(line, "");
1021 
1022   for (i = 0; i < smsg.hdr.total_dfields; i++) {
1023     line = AddLineF(line, "dfield[%02u].type   : %02X", i, smsg.dfield[i].type);
1024     line = AddLineF(line, "dfield[%02u].offset : %u", i, smsg.dfield[i].offset);
1025     line = AddLineF(line, "dfield[%02u].length : %u", i, smsg.dfield[i].length);
1026   }
1027 
1028   line = AddLine(line, "");
1029   AddLineF(line, "hfields:");
1030   line = AddLine(line, "");
1031 
1032   for (i = 0; i < smsg.total_hfields; i++) {
1033     line = AddLineF(line, "hfield[%02u].type   : %02X", i, smsg.hfield[i].type);
1034     line = AddLineF(line, "hfield[%02u].length : %d", i, smsg.hfield[i].length);
1035     line = AddLineF(line, "hfield_dat[%02u]    : %s", i, binstr((char *)smsg.hfield_dat[i], smsg.hfield[i].length));
1036   }
1037 
1038   line = AddLine(line, "");
1039   AddLineF(line, lng_head);
1040   line = AddLine(line, "");
1041 
1042   int _count = 0;
1043   char* _ptr = (char*)&smsg.hdr;
1044   while(_count < sizeof(msghdr_t)) {
1045     sprintf(buf, "%04X   ", _count);
1046     HexDump16(buf+7, _ptr, 16, HEX_DUMP2);
1047     line = AddLine(line, buf);
1048     _count += 16;
1049     _ptr += 16;
1050   }
1051   sprintf(buf, "%04X   ", _count);
1052   HexDump16(buf+7, _ptr, sizeof(msghdr_t)%16, HEX_DUMP2);
1053   line = AddLine(line, buf);
1054 
1055   uint16_t xlat;
1056   uint8_t  *inbuf;
1057   int32_t  outlen;
1058   bool lzh;
1059   bool tail = true;
1060   uint32_t l;
1061   uint32_t txt_len = 1;
1062 
1063   msg->txt = throw_strdup("");
1064 
1065   for(i = 0; i < smsg.hdr.total_dfields; i++)
1066     switch(smsg.dfield[i].type) {
1067       case TEXT_BODY:
1068         if(tail)
1069           tail = false;
1070         goto common;
1071       case TEXT_TAIL:
1072         if(not tail) {
1073           tail = true;
1074           msg->txt[txt_len-1] = '\r';
1075           txt_len++;
1076         }
1077 common:
1078         fseek(data->sdt_fp, smsg.hdr.offset + smsg.dfield[i].offset, SEEK_SET);
1079         l = sizeof(xlat);
1080         fread(&xlat, sizeof(xlat), 1, data->sdt_fp);
1081         lzh = false;
1082         if(xlat == XLAT_LZH) {
1083           lzh = true;
1084           fread(&xlat, sizeof(xlat), 1, data->sdt_fp);
1085           l += sizeof(xlat);
1086         }
1087         if(xlat != XLAT_NONE) /* no other translations currently supported */
1088           continue;
1089         if(lzh) {
1090           inbuf = (uint8_t *)throw_xmalloc(smsg.dfield[i].length);
1091           fread(inbuf, smsg.dfield[i].length - l, 1, data->sdt_fp);
1092           outlen = *(int32_t *)inbuf;
1093           msg->txt = (char *)throw_realloc(msg->txt, txt_len+outlen);
1094           lzh_decode(inbuf, smsg.dfield[i].length - l, (uint8_t *)(msg->txt+txt_len-1));
1095           throw_xfree(inbuf);
1096         }
1097         else if(l < smsg.dfield[i].length) {
1098           outlen = smsg.dfield[i].length - l;
1099           msg->txt = (char *)throw_realloc(msg->txt, txt_len+outlen);
1100           fread(msg->txt+txt_len-1, smsg.dfield[i].length - l, 1, data->sdt_fp);
1101         }
1102         else
1103           outlen = 0;
1104         txt_len+=outlen;
1105         msg->txt[txt_len-1] = NUL;
1106         break;
1107     }
1108 
1109   smb_freemsgmem(&smsg);
1110 
1111   GFTRK(0);
1112 
1113   return line;
1114 }
1115 
1116 
1117 //  ------------------------------------------------------------------
1118