|
|
1.1 root 1: /* postmsg.cpp */
2:
3: /* Synchronet user create/post public message routine */
4:
5: /* $Id: postmsg.cpp,v 1.69 2006/08/23 22:34:32 rswindell Exp $ */
6:
7: /****************************************************************************
8: * @format.tab-size 4 (Plain Text/Source Code File Header) *
9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
10: * *
11: * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html *
12: * *
13: * This program is free software; you can redistribute it and/or *
14: * modify it under the terms of the GNU General Public License *
15: * as published by the Free Software Foundation; either version 2 *
16: * of the License, or (at your option) any later version. *
17: * See the GNU General Public License for more details: gpl.txt or *
18: * http://www.fsf.org/copyleft/gpl.html *
19: * *
20: * Anonymous FTP access to the most recent released source is available at *
21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
22: * *
23: * Anonymous CVS access to the development source and modification history *
24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
26: * (just hit return, no password is necessary) *
27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
28: * *
29: * For Synchronet coding style and modification guidelines, see *
30: * http://www.synchro.net/source.html *
31: * *
32: * You are encouraged to submit any modifications (preferably in Unix diff *
33: * format) via e-mail to [email protected] *
34: * *
35: * Note: If this box doesn't appear square, then you need to fix your tabs. *
36: ****************************************************************************/
37:
38: #include "sbbs.h"
39:
40: static char* program_id(char* pid)
41: {
42: char compiler[64];
43:
44: DESCRIBE_COMPILER(compiler);
45: sprintf(pid,"%.10s %s%c-%s%s%s %s %s"
46: ,VERSION_NOTICE,VERSION,REVISION,PLATFORM_DESC
47: ,beta_version
48: #ifdef _DEBUG
49: ," Debug"
50: #else
51: ,""
52: #endif
53: ,__DATE__,compiler);
54: return(pid);
55: }
56:
57: /****************************************************************************/
58: /* Posts a message on subboard number sub, with 'top' as top of message. */
59: /* Returns 1 if posted, 0 if not. */
60: /****************************************************************************/
61: bool sbbs_t::postmsg(uint subnum, smbmsg_t *remsg, long wm_mode)
62: {
63: char str[256],title[LEN_TITLE+1],buf[SDT_BLOCK_LEN],top[256];
64: char msg_id[256];
65: char touser[64];
66: char from[64];
67: char pid[128];
68: ushort xlat,msgattr;
69: int i,j,x,file,storage;
70: ulong length,offset,crc=0xffffffff;
71: FILE* instream;
72: smbmsg_t msg;
73:
74: if(remsg) {
75: sprintf(title,"%.*s",LEN_TITLE,remsg->subj);
76: if(remsg->hdr.attr&MSG_ANONYMOUS)
77: SAFECOPY(from,text[Anonymous]);
78: else
79: SAFECOPY(from,remsg->from);
80: // If user posted this message, reply to the original recipient again
81: if((remsg->from_ext!=NULL && atoi(remsg->from_ext)==useron.number)
82: || stricmp(useron.alias,remsg->from)==0 || stricmp(useron.name,remsg->from)==0)
83: SAFECOPY(touser,remsg->to);
84: else
85: SAFECOPY(touser,from);
86: msgattr=(ushort)(remsg->hdr.attr&MSG_PRIVATE);
87: sprintf(top,text[RegardingByToOn],title,from,remsg->to
88: ,timestr((time_t *)&remsg->hdr.when_written.time)
89: ,smb_zonestr(remsg->hdr.when_written.zone,NULL));
90: } else {
91: title[0]=0;
92: touser[0]=0;
93: top[0]=0;
94: msgattr=0;
95: }
96:
97: /* Security checks */
98: if(!chk_ar(cfg.sub[subnum]->post_ar,&useron)) {
99: bputs(text[CantPostOnSub]);
100: return(false);
101: }
102: if(useron.rest&FLAG('P')) {
103: bputs(text[R_Post]);
104: return(false);
105: }
106: if((cfg.sub[subnum]->misc&(SUB_QNET|SUB_FIDO|SUB_PNET|SUB_INET))
107: && (useron.rest&FLAG('N'))) {
108: bputs(text[CantPostOnSub]);
109: return(false);
110: }
111: if(useron.ptoday>=cfg.level_postsperday[useron.level]) {
112: bputs(text[TooManyPostsToday]);
113: return(false);
114: }
115:
116: bprintf(text[Posting],cfg.grp[cfg.sub[subnum]->grp]->sname,cfg.sub[subnum]->lname);
117: action=NODE_PMSG;
118: nodesync();
119:
120: if(!(msgattr&MSG_PRIVATE) && (cfg.sub[subnum]->misc&SUB_PONLY
121: || (cfg.sub[subnum]->misc&SUB_PRIV && !noyes(text[PrivatePostQ]))))
122: msgattr|=MSG_PRIVATE;
123:
124: if(sys_status&SS_ABORT)
125: return(false);
126:
127: if(
128: #if 0 /* we *do* support internet posts to specific people July-11-2002 */
129: !(cfg.sub[subnum]->misc&SUB_INET) && // Prompt for TO: user
130: #endif
131: (cfg.sub[subnum]->misc&SUB_TOUSER || msgattr&MSG_PRIVATE || touser[0])) {
132: if(!touser[0] && !(msgattr&MSG_PRIVATE))
133: SAFECOPY(touser,"All");
134: bputs(text[PostTo]);
135: i=LEN_ALIAS;
136: if(cfg.sub[subnum]->misc&SUB_QNET)
137: i=25;
138: if(cfg.sub[subnum]->misc&SUB_FIDO)
139: i=FIDO_NAME_LEN-1;
140: if(cfg.sub[subnum]->misc&(SUB_PNET|SUB_INET))
141: i=60;
142: getstr(touser,i,K_UPRLWR|K_LINE|K_EDIT|K_AUTODEL);
143: if(stricmp(touser,"ALL")
144: && !(cfg.sub[subnum]->misc&(SUB_PNET|SUB_FIDO|SUB_QNET|SUB_INET|SUB_ANON))) {
145: if(cfg.sub[subnum]->misc&SUB_NAME) {
146: if(!userdatdupe(useron.number,U_NAME,LEN_NAME,touser,0)) {
147: bputs(text[UnknownUser]);
148: return(false);
149: }
150: }
151: else {
152: if((i=finduser(touser))==0)
153: return(false);
154: username(&cfg,i,touser);
155: }
156: }
157: if(sys_status&SS_ABORT)
158: return(false);
159: }
160:
161: if(!touser[0])
162: SAFECOPY(touser,"All"); // Default to ALL
163:
164: if(!stricmp(touser,"SYSOP") && !SYSOP) // Change SYSOP to user #1
165: username(&cfg,1,touser);
166:
167: if(msgattr&MSG_PRIVATE && !stricmp(touser,"ALL")) {
168: bputs(text[NoToUser]);
169: return(false);
170: }
171: if(msgattr&MSG_PRIVATE)
172: wm_mode|=WM_PRIVATE;
173:
174: if(cfg.sub[subnum]->misc&SUB_AONLY
175: || (cfg.sub[subnum]->misc&SUB_ANON && useron.exempt&FLAG('A')
176: && !noyes(text[AnonymousQ])))
177: msgattr|=MSG_ANONYMOUS;
178:
179: if(cfg.sub[subnum]->mod_ar[0] && chk_ar(cfg.sub[subnum]->mod_ar,&useron))
180: msgattr|=MSG_MODERATED;
181:
182: if(cfg.sub[subnum]->misc&SUB_SYSPERM && sub_op(subnum))
183: msgattr|=MSG_PERMANENT;
184:
185: if(msgattr&MSG_PRIVATE)
186: bputs(text[PostingPrivately]);
187:
188: if(msgattr&MSG_ANONYMOUS)
189: bputs(text[PostingAnonymously]);
190:
191: if(cfg.sub[subnum]->misc&SUB_NAME)
192: bputs(text[UsingRealName]);
193:
194: msg_tmp_fname(useron.xedit, str, sizeof(str));
195: if(!writemsg(str,top,title,wm_mode,subnum,touser)
196: || (long)(length=flength(str))<1) { /* Bugfix Aug-20-2003: Reject negative length */
197: bputs(text[Aborted]);
198: return(false);
199: }
200:
201: bputs(text[WritingIndx]);
202:
203: if((i=smb_stack(&smb,SMB_STACK_PUSH))!=SMB_SUCCESS) {
204: errormsg(WHERE,ERR_OPEN,cfg.sub[subnum]->code,i,smb.last_error);
205: return(false);
206: }
207:
208: sprintf(smb.file,"%s%s",cfg.sub[subnum]->data_dir,cfg.sub[subnum]->code);
209: smb.retry_time=cfg.smb_retry_time;
210: smb.subnum=subnum;
211: if((i=smb_open(&smb))!=SMB_SUCCESS) {
212: errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
213: smb_stack(&smb,SMB_STACK_POP);
214: return(false);
215: }
216:
217: if(filelength(fileno(smb.shd_fp))<1) { /* Create it if it doesn't exist */
218: smb.status.max_crcs=cfg.sub[subnum]->maxcrcs;
219: smb.status.max_msgs=cfg.sub[subnum]->maxmsgs;
220: smb.status.max_age=cfg.sub[subnum]->maxage;
221: smb.status.attr=cfg.sub[subnum]->misc&SUB_HYPER ? SMB_HYPERALLOC : 0;
222: if((i=smb_create(&smb))!=SMB_SUCCESS) {
223: smb_close(&smb);
224: errormsg(WHERE,ERR_CREATE,smb.file,i,smb.last_error);
225: smb_stack(&smb,SMB_STACK_POP);
226: return(false);
227: }
228: }
229:
230: if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS) {
231: smb_close(&smb);
232: errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);
233: smb_stack(&smb,SMB_STACK_POP);
234: return(false);
235: }
236:
237: if((i=smb_getstatus(&smb))!=SMB_SUCCESS) {
238: smb_close(&smb);
239: errormsg(WHERE,ERR_READ,smb.file,i,smb.last_error);
240: smb_stack(&smb,SMB_STACK_POP);
241: return(false);
242: }
243:
244: length+=sizeof(xlat); /* +2 for translation string */
245:
246: if(length&0xfff00000UL) {
247: smb_close(&smb);
248: errormsg(WHERE,ERR_LEN,str,length,smb.last_error);
249: smb_stack(&smb,SMB_STACK_POP);
250: return(false);
251: }
252:
253: if(smb.status.attr&SMB_HYPERALLOC) {
254: offset=smb_hallocdat(&smb);
255: storage=SMB_HYPERALLOC;
256: }
257: else {
258: if((i=smb_open_da(&smb))!=SMB_SUCCESS) {
259: smb_close(&smb);
260: errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
261: smb_stack(&smb,SMB_STACK_POP);
262: return(false);
263: }
264: if(cfg.sub[subnum]->misc&SUB_FAST) {
265: offset=smb_fallocdat(&smb,length,1);
266: storage=SMB_FASTALLOC;
267: }
268: else {
269: offset=smb_allocdat(&smb,length,1);
270: storage=SMB_SELFPACK;
271: }
272: smb_close_da(&smb);
273: }
274:
275: if((file=open(str,O_RDONLY|O_BINARY))==-1
276: || (instream=fdopen(file,"rb"))==NULL) {
277: smb_freemsgdat(&smb,offset,length,1);
278: smb_close(&smb);
279: errormsg(WHERE,ERR_OPEN,str,O_RDONLY|O_BINARY);
280: smb_stack(&smb,SMB_STACK_POP);
281: return(false);
282: }
283:
284: setvbuf(instream,NULL,_IOFBF,2*1024);
285: fseek(smb.sdt_fp,offset,SEEK_SET);
286: xlat=XLAT_NONE;
287: fwrite(&xlat,2,1,smb.sdt_fp);
288: x=SDT_BLOCK_LEN-2; /* Don't read/write more than 255 */
289: while(!feof(instream)) {
290: memset(buf,0,x);
291: j=fread(buf,1,x,instream);
292: if(j<1)
293: break;
294: if(j>1 && (j!=x || feof(instream)) && buf[j-1]==LF && buf[j-2]==CR)
295: buf[j-1]=buf[j-2]=0; /* Convert to NULL */
296: if(cfg.sub[subnum]->maxcrcs) {
297: for(i=0;i<j;i++)
298: crc=ucrc32(buf[i],crc);
299: }
300: fwrite(buf,j,1,smb.sdt_fp);
301: x=SDT_BLOCK_LEN;
302: }
303: fflush(smb.sdt_fp);
304: fclose(instream);
305: crc=~crc;
306:
307: memset(&msg,0,sizeof(smbmsg_t));
308: msg.hdr.version=smb_ver();
309: msg.hdr.attr=msgattr;
310: msg.hdr.when_written.time=msg.hdr.when_imported.time=time(NULL);
311: msg.hdr.when_written.zone=msg.hdr.when_imported.zone=sys_timezone(&cfg);
312:
313: msg.hdr.number=smb.status.last_msg+1; /* this *should* be the new message number */
314:
315: smb_hfield_str(&msg,FIDOPID,program_id(pid));
316:
317: /* Generate default (RFC822) message-id (always) */
318: SAFECOPY(msg_id,get_msgid(&cfg,subnum,&msg));
319: smb_hfield_str(&msg,RFC822MSGID,msg_id);
320:
321: /* Generate FTN (FTS-9) MSGID */
322: if(cfg.sub[subnum]->misc&SUB_FIDO) {
323: SAFECOPY(msg_id,ftn_msgid(cfg.sub[subnum],&msg));
324: smb_hfield_str(&msg,FIDOMSGID,msg_id);
325: }
326: if(remsg) {
327:
328: msg.hdr.thread_back=remsg->hdr.number; /* needed for threading backward */
329:
330: /* Add RFC-822 Reply-ID (generate if necessary) */
331: if(remsg->id!=NULL)
332: smb_hfield_str(&msg,RFC822REPLYID,remsg->id);
333:
334: /* Add FidoNet Reply if original message has FidoNet MSGID */
335: if(remsg->ftn_msgid!=NULL)
336: smb_hfield_str(&msg,FIDOREPLYID,remsg->ftn_msgid);
337:
338: if((i=smb_updatethread(&smb, remsg, smb.status.last_msg+1))!=SMB_SUCCESS)
339: errormsg(WHERE,ERR_WRITE,smb.file,i,smb.last_error);
340: }
341:
342:
343: if(cfg.sub[subnum]->maxcrcs) {
344: i=smb_addcrc(&smb,crc);
345: if(i) {
346: smb_freemsgdat(&smb,offset,length,1);
347: smb_close(&smb);
348: smb_stack(&smb,SMB_STACK_POP);
349: attr(cfg.color[clr_err]);
350: bputs("Duplicate message!\r\n");
351: return(false);
352: }
353: }
354:
355: msg.hdr.offset=offset;
356:
357: smb_hfield_str(&msg,RECIPIENT,touser);
358: strlwr(touser);
359:
360: SAFECOPY(str,cfg.sub[subnum]->misc&SUB_NAME ? useron.name : useron.alias);
361: smb_hfield_str(&msg,SENDER,str);
362: strlwr(str);
363:
364: sprintf(str,"%u",useron.number);
365: smb_hfield_str(&msg,SENDEREXT,str);
366:
367: /* Security logging */
368: msg_client_hfields(&msg,&client);
369:
370: smb_hfield_str(&msg,SUBJECT,title);
371:
372: smb_dfield(&msg,TEXT_BODY,length);
373:
374: i=smb_addmsghdr(&smb,&msg,storage); // calls smb_unlocksmbhdr()
375: smb_close(&smb);
376: smb_stack(&smb,SMB_STACK_POP);
377:
378: smb_freemsgmem(&msg);
379: if(i!=SMB_SUCCESS) {
380: errormsg(WHERE,ERR_WRITE,smb.file,i,smb.last_error);
381: smb_freemsgdat(&smb,offset,length,1);
382: return(false);
383: }
384:
385: logon_posts++;
386: user_posted_msg(&cfg, &useron, 1);
387: bprintf(text[Posted],cfg.grp[cfg.sub[subnum]->grp]->sname
388: ,cfg.sub[subnum]->lname);
389: sprintf(str,"%s posted on %s %s"
390: ,useron.alias,cfg.grp[cfg.sub[subnum]->grp]->sname,cfg.sub[subnum]->lname);
391: logline("P+",str);
392:
393: signal_sub_sem(&cfg,subnum);
394:
395: user_event(EVENT_POST);
396:
397: return(true);
398: }
399:
400: extern "C" void DLLCALL signal_sub_sem(scfg_t* cfg, uint subnum)
401: {
402: char str[MAX_PATH+1];
403:
404: if(subnum==INVALID_SUB || subnum>=cfg->total_subs) /* e-mail? */
405: return;
406:
407: /* signal semaphore files */
408: if(cfg->sub[subnum]->misc&SUB_FIDO && cfg->echomail_sem[0])
409: ftouch(cmdstr(cfg,NULL,cfg->echomail_sem,nulstr,nulstr,str));
410: if(cfg->sub[subnum]->post_sem[0])
411: ftouch(cmdstr(cfg,NULL,cfg->sub[subnum]->post_sem,nulstr,nulstr,str));
412: }
413:
414: extern "C" int DLLCALL msg_client_hfields(smbmsg_t* msg, client_t* client)
415: {
416: int i;
417:
418: if(client==NULL)
419: return(-1);
420:
421: if((i=smb_hfield_str(msg,SENDERIPADDR,client->addr))!=SMB_SUCCESS)
422: return(i);
423: if((i=smb_hfield_str(msg,SENDERHOSTNAME,client->host))!=SMB_SUCCESS)
424: return(i);
425: if((i=smb_hfield_str(msg,SENDERPROTOCOL,client->protocol))!=SMB_SUCCESS)
426: return(i);
427: return smb_hfield(msg,SENDERPORT,sizeof(client->port),&client->port);
428: }
429:
430: extern "C" int DLLCALL savemsg(scfg_t* cfg, smb_t* smb, smbmsg_t* msg, client_t* client, char* msgbuf)
431: {
432: char pid[128];
433: char msg_id[256];
434: ushort xlat=XLAT_NONE;
435: int i;
436: int storage=SMB_SELFPACK;
437: long dupechk_hashes=SMB_HASH_SOURCE_ALL;
438:
439: if(msg==NULL)
440: return(SMB_FAILURE);
441:
442: if(!SMB_IS_OPEN(smb)) {
443: if(smb->subnum==INVALID_SUB)
444: sprintf(smb->file,"%smail",cfg->data_dir);
445: else
446: sprintf(smb->file,"%s%s",cfg->sub[smb->subnum]->data_dir,cfg->sub[smb->subnum]->code);
447: smb->retry_time=cfg->smb_retry_time;
448: if((i=smb_open(smb))!=SMB_SUCCESS)
449: return(i);
450: }
451:
452: /* Lock the msgbase early to preserve our message number (used in MSG-IDs) */
453: if(!smb->locked && smb_locksmbhdr(smb)!=SMB_SUCCESS)
454: return(SMB_ERR_LOCK);
455:
456: if(filelength(fileno(smb->shd_fp))>0 && (i=smb_getstatus(smb))!=SMB_SUCCESS) {
457: if(smb->locked)
458: smb_unlocksmbhdr(smb);
459: return(i);
460: }
461:
462: if(smb->subnum==INVALID_SUB) { /* e-mail */
463:
464: smb->status.max_crcs=cfg->mail_maxcrcs;
465: smb->status.max_age=cfg->mail_maxage;
466: smb->status.max_msgs=0; /* unlimited */
467: smb->status.attr=SMB_EMAIL;
468:
469: if(cfg->sys_misc&SM_FASTMAIL)
470: storage=SMB_FASTALLOC;
471:
472: /* duplicate message-IDs must be allowed in mail database */
473: dupechk_hashes&=~(1<<SMB_HASH_SOURCE_MSG_ID);
474:
475: } else { /* sub-board */
476:
477: smb->status.max_crcs=cfg->sub[smb->subnum]->maxcrcs;
478: smb->status.max_msgs=cfg->sub[smb->subnum]->maxmsgs;
479: smb->status.max_age=cfg->sub[smb->subnum]->maxage;
480: smb->status.attr=0;
481:
482: if(cfg->sub[smb->subnum]->misc&SUB_HYPER)
483: storage = smb->status.attr = SMB_HYPERALLOC;
484: else if(cfg->sub[smb->subnum]->misc&SUB_FAST)
485: storage = SMB_FASTALLOC;
486:
487: if(cfg->sub[smb->subnum]->misc&SUB_LZH)
488: xlat=XLAT_LZH;
489:
490: /* enforce anonymous/private posts only */
491: if(cfg->sub[smb->subnum]->misc&SUB_PONLY)
492: msg->hdr.attr|=MSG_PRIVATE;
493: if(cfg->sub[smb->subnum]->misc&SUB_AONLY)
494: msg->hdr.attr|=MSG_ANONYMOUS;
495: }
496:
497: if(msg->hdr.when_imported.time==0) {
498: msg->hdr.when_imported.time=time(NULL);
499: msg->hdr.when_imported.zone=sys_timezone(cfg);
500: }
501: if(msg->hdr.when_written.time==0) /* Uninitialized */
502: msg->hdr.when_written = msg->hdr.when_imported;
503:
504: msg->hdr.number=smb->status.last_msg+1; /* needed for MSG-ID generation */
505:
506: if(smb->status.max_crcs==0) /* no CRC checking means no body text dupe checking */
507: dupechk_hashes&=~(1<<SMB_HASH_SOURCE_BODY);
508:
509: if(client!=NULL)
510: msg_client_hfields(msg,client);
511:
512: /* Generate FidoNet Program Identifier */
513: if(msg->ftn_pid==NULL)
514: smb_hfield_str(msg,FIDOPID,program_id(pid));
515:
516: /* Generate RFC-822 Message-id */
517: if(msg->id==NULL) {
518: SAFECOPY(msg_id,get_msgid(cfg,smb->subnum,msg));
519: smb_hfield_str(msg,RFC822MSGID,msg_id);
520: }
521:
522: /* Generate FidoNet MSGID (for FidoNet sub-boards) */
523: if(smb->subnum!=INVALID_SUB && cfg->sub[smb->subnum]->misc&SUB_FIDO
524: && msg->ftn_msgid==NULL) {
525: SAFECOPY(msg_id,ftn_msgid(cfg->sub[smb->subnum],msg));
526: smb_hfield_str(msg,FIDOMSGID,msg_id);
527: }
528:
529: if((i=smb_addmsg(smb,msg,storage,dupechk_hashes,xlat,(uchar*)msgbuf,NULL))==SMB_SUCCESS)
530: signal_sub_sem(cfg,smb->subnum);
531:
532: return(i);
533: }
534:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.