diff --git a/src/sbbs3/atcodes.cpp b/src/sbbs3/atcodes.cpp index bb63c77587ab57d7ae74e0caefb798a154ab36ac..5ba3bcc0cd2e37a7f93ddbca076a10b1a1688511 100644 --- a/src/sbbs3/atcodes.cpp +++ b/src/sbbs3/atcodes.cpp @@ -1,5 +1,3 @@ -/* atcodes.cpp */ - /* Synchronet "@code" functions */ /* $Id$ */ @@ -1053,6 +1051,18 @@ const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen) safe_snprintf(str,maxlen,"%lu",current_msg->hdr.number); return(str); } + if(!strcmp(sp,"MSG_SCORE") && current_msg!=NULL) { + safe_snprintf(str, maxlen, "%ld", current_msg->upvotes - current_msg->downvotes); + return(str); + } + if(!strcmp(sp,"MSG_UPVOTES") && current_msg!=NULL) { + safe_snprintf(str, maxlen, "%lu", current_msg->upvotes); + return(str); + } + if(!strcmp(sp,"MSG_DOWNVOTES") && current_msg!=NULL) { + safe_snprintf(str, maxlen, "%lu", current_msg->downvotes); + return(str); + } if(!strcmp(sp,"SMB_AREA")) { if(smb.subnum!=INVALID_SUB && smb.subnum<cfg.total_subs) diff --git a/src/sbbs3/chksmb.c b/src/sbbs3/chksmb.c index bb5f3ee5457061aa9adfaa233f8d87fdd25c3a12..a6e95edff385ff7c22af011f28399a52ca94c869 100644 --- a/src/sbbs3/chksmb.c +++ b/src/sbbs3/chksmb.c @@ -1,5 +1,3 @@ -/* chksmb.c */ - /* Synchronet message base (SMB) validity checker */ /* $Id$ */ @@ -8,7 +6,7 @@ * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * - * Copyright 2012 Rob Swindell - http://www.synchro.net/copyright.html * + * Copyright Rob Swindell - http://www.synchro.net/copyright.html * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * @@ -432,7 +430,8 @@ int main(int argc, char **argv) "index import date/time\n"); timeerr++; } - if(msg.idx.subj!=smb_subject_crc(msg.subj)) { + if(msg.hdr.type != SMB_MSG_TYPE_VOTE + && msg.idx.subj!=smb_subject_crc(msg.subj)) { fprintf(stderr,"%sSubject CRC mismatch\n",beep); msgerr=TRUE; if(extinfo) @@ -453,6 +452,7 @@ int main(int argc, char **argv) fromcrc++; } if(!(smb.status.attr&SMB_EMAIL) + && msg.hdr.type != SMB_MSG_TYPE_VOTE && msg.idx.from!=smb_name_crc(msg.from)) { fprintf(stderr,"%sFrom CRC mismatch\n",beep); msgerr=TRUE; @@ -474,6 +474,7 @@ int main(int argc, char **argv) tocrc++; } if(!(smb.status.attr&SMB_EMAIL) + && msg.hdr.type != SMB_MSG_TYPE_VOTE && msg.to_ext==NULL && msg.idx.to!=smb_name_crc(msg.to)) { fprintf(stderr,"%sTo CRC mismatch\n",beep); msgerr=TRUE; diff --git a/src/sbbs3/fixsmb.c b/src/sbbs3/fixsmb.c index fb7b3f3e3cdf7b0ba320ea2a03e6d81a361ed6b9..888448de6b356773c6fccb86744c07af3deff6cd 100644 --- a/src/sbbs3/fixsmb.c +++ b/src/sbbs3/fixsmb.c @@ -1,5 +1,3 @@ -/* fixsmb.c */ - /* Synchronet message base (SMB) index re-generator */ /* $Id$ */ @@ -8,7 +6,7 @@ * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * - * Copyright 2015 Rob Swindell - http://www.synchro.net/copyright.html * + * Copyright Rob Swindell - http://www.synchro.net/copyright.html * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * diff --git a/src/sbbs3/readmsgs.cpp b/src/sbbs3/readmsgs.cpp index 5bfa29fc3725aaee12b7a17a5d97e02897312688..07bce84f3e39a177a9584b76d4716c6ed3f3274a 100644 --- a/src/sbbs3/readmsgs.cpp +++ b/src/sbbs3/readmsgs.cpp @@ -229,15 +229,12 @@ post_t * sbbs_t::loadposts(uint32_t *posts, uint subnum, ulong ptr, long mode, u rewind(smb.sid_fp); alloc_len=sizeof(post_t)*total; - #ifdef __OS2__ - while(alloc_len%4096) - alloc_len++; - #endif if((post=(post_t *)malloc(alloc_len))==NULL) { /* alloc for max */ smb_unlocksmbhdr(&smb); errormsg(WHERE,ERR_ALLOC,smb.file,sizeof(post_t *)*cfg.sub[subnum]->maxmsgs); return(NULL); } + memset(post, 0, alloc_len); if(unvalidated_num) *unvalidated_num=ULONG_MAX; @@ -268,6 +265,20 @@ post_t * sbbs_t::loadposts(uint32_t *posts, uint subnum, ulong ptr, long mode, u break; } + if(idx.attr&(MSG_UPVOTE|MSG_DOWNVOTE)) { + ulong u; + for(u = 0; u < l; u++) + if(post[u].idx.number == idx.msgnum) + break; + if(u < l) { + if(idx.attr&MSG_UPVOTE) + post[u].upvotes++; + else + post[u].downvotes++; + } + continue; + } + if(idx.attr&MSG_PRIVATE && !(mode&LP_PRIVATE) && !sub_op(subnum) && !(useron.rest&FLAG('Q'))) { if(idx.to!=namecrc && idx.from!=namecrc @@ -643,6 +654,8 @@ int sbbs_t::scanposts(uint subnum, long mode, const char *find) if(!reads && mode) CRLF; + msg.upvotes = post[smb.curmsg].upvotes; + msg.downvotes = post[smb.curmsg].downvotes; show_msg(&msg ,msg.from_ext && !strcmp(msg.from_ext,"1") && !msg.from_net.type ? 0:P_NOATCODES); @@ -737,7 +750,7 @@ int sbbs_t::scanposts(uint subnum, long mode, const char *find) bprintf(text[UnvalidatedWarning],unvalidated+1); bprintf(text[ReadingSub],ugrp,cfg.grp[cfg.sub[subnum]->grp]->sname ,usub,cfg.sub[subnum]->sname,smb.curmsg+1,smb.msgs); - sprintf(str,"ABCDEFILMNPQRTUY?<>[]{}-+()"); + sprintf(str,"ABCDEFILMNPQRTUVY?<>[]{}-+()"); if(sub_op(subnum)) strcat(str,"O"); do_find=true; @@ -999,6 +1012,33 @@ int sbbs_t::scanposts(uint subnum, long mode, const char *find) if(!showposts_toyou(subnum, post,0,smb.msgs, SCAN_UNREAD)) bputs(text[NoMessagesFound]); break; + case 'V': /* Vote in reply to message */ + { + smbmsg_t vote; + + if(smb_voted_already(&smb, msg.hdr.number + ,cfg.sub[subnum]->misc&SUB_NAME ? useron.name : useron.alias, NET_NONE, NULL)) { + bputs(text[No]); + break; + } + ZERO_VAR(vote); + vote.hdr.attr = MSG_UPVOTE; + vote.hdr.thread_back = msg.hdr.number; + vote.hdr.when_written.time = msg.hdr.when_imported.time = time32(NULL); + vote.hdr.when_written.zone = msg.hdr.when_imported.zone = sys_timezone(&cfg); + + smb_hfield_str(&vote, SENDER, cfg.sub[subnum]->misc&SUB_NAME ? useron.name : useron.alias); + + sprintf(str, "%u", useron.number); + smb_hfield_str(&vote, SENDEREXT, str); + + /* Security logging */ + msg_client_hfields(&vote, &client); + smb_hfield_str(&vote, SENDERSERVER, startup->host_name); + + smb_addvote(&smb, &vote, smb_storage_mode(&cfg, &smb)); + break; + } case '-': if(smb.curmsg>0) smb.curmsg--; do_find=false; diff --git a/src/sbbs3/sbbsdefs.h b/src/sbbs3/sbbsdefs.h index 5539d9b72821ff109108a602ea0a150625e01228..3c460cc6b8e3fe49515ae162ec5786dc5ed7c4d3 100644 --- a/src/sbbs3/sbbsdefs.h +++ b/src/sbbs3/sbbsdefs.h @@ -1,5 +1,3 @@ -/* sbbsdefs.h */ - /* Synchronet constants, macros, and structure definitions */ /* $Id$ */ @@ -997,6 +995,8 @@ typedef struct { /* File (transfers) Data */ typedef struct { idxrec_t idx; /* defined in smbdefs.h */ uint32_t num; /* 1-based offset */ + uint32_t upvotes; + uint32_t downvotes; } post_t; typedef idxrec_t mail_t; /* defined in smbdefs.h */ typedef fidoaddr_t faddr_t; /* defined in smbdefs.h */ diff --git a/src/smblib/smbadd.c b/src/smblib/smbadd.c index 56f6487132b35180e90cbc3bc618bf639ed85103..561f7e42392175edb5fb7857da05242c1e9628b5 100644 --- a/src/smblib/smbadd.c +++ b/src/smblib/smbadd.c @@ -1,5 +1,3 @@ -/* smbadd.c */ - /* Synchronet message base (SMB) high-level "add message" function */ /* $Id$ */ @@ -319,3 +317,59 @@ int SMBCALL smb_addmsg(smb_t* smb, smbmsg_t* msg, int storage, long dupechk_hash return(retval); } + +int SMBCALL smb_addvote(smb_t* smb, smbmsg_t* msg, int storage) +{ + int retval; + smbmsg_t remsg; + + if(!SMB_IS_OPEN(smb)) { + safe_snprintf(smb->last_error, sizeof(smb->last_error), "msgbase not open"); + return SMB_ERR_NOT_OPEN; + } + + if(filelength(fileno(smb->shd_fp)) < 1) { /* Create it if it doesn't exist */ + /* smb->status.max_crcs, max_msgs, max_age, and attr should be pre-initialized */ + if((retval=smb_create(smb))!=SMB_SUCCESS) + return retval; + } + + if(!smb->locked && smb_locksmbhdr(smb) != SMB_SUCCESS) + return SMB_ERR_LOCK; + + msg->hdr.total_dfields = 0; + + if((retval=smb_getstatus(smb)) != SMB_SUCCESS) { + smb_unlocksmbhdr(smb); + return retval; + } + + msg->hdr.type = SMB_MSG_TYPE_VOTE; + msg->hdr.number = smb->status.last_msg+1; + + if(msg->hdr.when_imported.time == 0) { + msg->hdr.when_imported.time = (uint32_t)time(NULL); + msg->hdr.when_imported.zone = 0; /* how do we detect system TZ? */ + } + if(msg->hdr.when_written.time == 0) /* Uninitialized */ + msg->hdr.when_written = msg->hdr.when_imported; + + /* Look-up thread_back if RFC822 Reply-ID was specified */ + if(msg->hdr.thread_back == 0 && msg->reply_id != NULL) { + if(smb_getmsgidx_by_msgid(smb, &remsg, msg->reply_id) == SMB_SUCCESS) + msg->hdr.thread_back = remsg.idx.number; /* needed for threading backward */ + } + + /* Look-up thread_back if FTN REPLY was specified */ + if(msg->hdr.thread_back == 0 && msg->ftn_reply != NULL) { + if(smb_getmsgidx_by_ftnid(smb, &remsg, msg->ftn_reply) == SMB_SUCCESS) + msg->hdr.thread_back = remsg.idx.number; /* needed for threading backward */ + } + + retval = smb_addmsghdr(smb, msg, storage); /* calls smb_unlocksmbhdr() */ + + if(smb->locked) + smb_unlocksmbhdr(smb); + + return retval; +} diff --git a/src/smblib/smbdefs.h b/src/smblib/smbdefs.h index 0912b2a6dab6dde7ec6e42282d090d4049d3880b..c1c28a40a379f9287dc9b79a2140883ec18b5122 100644 --- a/src/smblib/smbdefs.h +++ b/src/smblib/smbdefs.h @@ -1,5 +1,3 @@ -/* smbdefs.h */ - /* Synchronet message base constant and structure definitions */ /* $Id$ */ @@ -338,6 +336,8 @@ #define MSG_VALIDATED (1<<8) #define MSG_REPLIED (1<<9) /* User replied to this message */ #define MSG_NOREPLY (1<<10) /* No replies (or bounces) should be sent to the sender */ +#define MSG_UPVOTE (1<<11) /* This message is an upvote */ +#define MSG_DOWNVOTE (1<<12) /* This message is a downvote */ /* Auxillary header attributes */ #define MSG_FILEREQUEST (1<<0) /* File request */ @@ -438,9 +438,17 @@ typedef struct _PACK { /* Time with time-zone */ typedef struct _PACK { /* Index record */ - uint16_t to; /* 16-bit CRC of recipient name (lower case) */ - uint16_t from; /* 16-bit CRC of sender name (lower case) */ - uint16_t subj; /* 16-bit CRC of subject (lower case, w/o RE:) */ + union { + struct { + uint16_t to; /* 16-bit CRC of recipient name (lower case) or user # */ + uint16_t from; /* 16-bit CRC of sender name (lower case) or user # */ + uint16_t subj; /* 16-bit CRC of subject (lower case, w/o RE:) */ + }; + struct { + uint16_t vote; /* vote value */ + uint32_t msgnum; /* number of message this vote is in response to */ + }; + }; uint16_t attr; /* attributes (read, permanent, etc.) */ uint32_t offset; /* offset into header file */ uint32_t number; /* number of message (1 based) */ @@ -515,14 +523,20 @@ typedef struct _PACK { /* Message base status header */ } smbstatus_t; +enum smb_msg_type { + SMB_MSG_TYPE_NORMAL /* Classic message (for reading) */ + ,SMB_MSG_TYPE_POLL /* A poll question */ + ,SMB_MSG_TYPE_VOTE /* Voter response to poll or normal message */ +}; + typedef struct _PACK { /* Message header */ /* 00 */ uchar id[LEN_HEADER_ID]; /* SHD<^Z> */ - /* 04 */ uint16_t type; /* Message type (normally 0) */ + /* 04 */ uint16_t type; /* Message type (enum smb_msg_type) */ /* 06 */ uint16_t version; /* Version of type (initially 100h for 1.00) */ /* 08 */ uint16_t length; /* Total length of fixed record + all fields */ /* 0a */ uint16_t attr; /* Attributes (bit field) (duped in SID) */ - /* 0c */ uint32_t auxattr; /* Auxillary attributes (bit field) */ + /* 0c */ uint32_t auxattr; /* Auxiliary attributes (bit field) */ /* 10 */ uint32_t netattr; /* Network attributes */ /* 14 */ when_t when_written; /* Date/time/zone message was written */ /* 1a */ when_t when_imported; /* Date/time/zone message was imported */ @@ -531,7 +545,7 @@ typedef struct _PACK { /* Message header */ /* 28 */ uint32_t thread_next; /* Next message in thread */ /* 2c */ uint32_t thread_first; /* First reply to this message */ /* 30 */ uint16_t delivery_attempts; /* Delivery attempt counter */ - /* 32 */ uchar reserved[2]; /* Reserved for future use */ + /* 32 */ int16_t vote; /* Vote value (response to poll) */ /* 34 */ uint32_t thread_id; /* Number of original message in thread (or 0 if unknown) */ /* 38 */ uint32_t times_downloaded; /* Total number of times downloaded (moved Mar-6-2012) */ /* 3c */ uint32_t last_downloaded; /* Date/time of last download (moved Mar-6-2012) */ @@ -624,6 +638,8 @@ typedef struct { /* Message */ uint32_t priority; /* Message priority (0 is lowest) */ uint32_t cost; /* Cost to download/read */ uint32_t flags; /* Various smblib run-time flags (see MSG_FLAG_*) */ + uint32_t upvotes; /* Vote tally for this message */ + uint32_t downvotes; /* Vote tally for this message */ } smbmsg_t; diff --git a/src/smblib/smblib.c b/src/smblib/smblib.c index 327b6d736404d3cea1c3f9dc197fb9ec28c474fb..20fd0ee4229244058388bdd2ba6ca86c47078175 100644 --- a/src/smblib/smblib.c +++ b/src/smblib/smblib.c @@ -1,5 +1,3 @@ -/* smblib.c */ - /* Synchronet message base (SMB) library routines */ /* $Id$ */ @@ -1636,6 +1634,9 @@ int SMBCALL smb_init_idx(smb_t* smb, smbmsg_t* msg) msg->idx.from=atoi(msg->from_ext); else msg->idx.from=0; + } else if(msg->hdr.type == SMB_MSG_TYPE_VOTE) { + msg->idx.vote = msg->hdr.vote; + msg->idx.msgnum = msg->hdr.thread_back; } else { msg->idx.to=smb_name_crc(msg->to); msg->idx.from=smb_name_crc(msg->from); @@ -1649,6 +1650,48 @@ int SMBCALL smb_init_idx(smb_t* smb, smbmsg_t* msg) return(SMB_SUCCESS); } +BOOL SMBCALL smb_voted_already(smb_t* smb, uint32_t msgnum, const char* name, enum smb_net_type net_type, void* net_addr) +{ + BOOL result = FALSE; + smbmsg_t msg; + + if(smb->sid_fp==NULL) { + safe_snprintf(smb->last_error, sizeof(smb->last_error), "index not open"); + return SMB_ERR_NOT_OPEN; + } + clearerr(smb->sid_fp); + if(fseek(smb->sid_fp,0,SEEK_SET)) { + safe_snprintf(smb->last_error, sizeof(smb->last_error) + ,"%d '%s' seeking to beginning of index file" + ,get_errno(), STRERROR(get_errno())); + return SMB_ERR_SEEK; + } + while(!result && smb_fread(smb, &msg.idx, sizeof(msg.idx), smb->sid_fp) == sizeof(msg.idx)) { + if(!(msg.idx.attr&(MSG_UPVOTE|MSG_DOWNVOTE))) + continue; + if(msg.idx.msgnum != msgnum) + continue; + if(smb_getmsghdr(smb, &msg) != SMB_SUCCESS) + continue; + if(stricmp(msg.from, name) == 0) { + if(msg.from_net.type == net_type) + switch(net_type) { + case NET_NONE: + result = TRUE; + break; + case NET_FIDO: + result = memcmp(msg.from_net.addr, net_addr, sizeof(fidoaddr_t)) == 0; + break; + default: + result = stricmp(msg.from_net.addr, net_addr) == 0; + break; + } + } + smb_freemsgmem(&msg); + } + return result; +} + /****************************************************************************/ /* Writes index information for 'msg' */ /* msg->idx */ diff --git a/src/smblib/smblib.h b/src/smblib/smblib.h index 20942268a5435e9b8d1200d7af1b63cf00b02de4..1f9f2242fc144719f5951be1a36fc33409153e15 100644 --- a/src/smblib/smblib.h +++ b/src/smblib/smblib.h @@ -1,5 +1,3 @@ -/* smblib.h */ - /* Synchronet message base (SMB) library function prototypes */ /* $Id$ */ @@ -156,10 +154,12 @@ SMBEXPORT int SMBCALL smb_updatethread(smb_t* smb, smbmsg_t* remsg, ulong newms SMBEXPORT int SMBCALL smb_updatemsg(smb_t* smb, smbmsg_t* msg); SMBEXPORT BOOL SMBCALL smb_valid_hdr_offset(smb_t* smb, ulong offset); SMBEXPORT int SMBCALL smb_init_idx(smb_t* smb, smbmsg_t* msg); +SMBEXPORT BOOL SMBCALL smb_voted_already(smb_t*, uint32_t msgnum, const char* name, enum smb_net_type, void* net_addr); /* smbadd.c */ SMBEXPORT int SMBCALL smb_addmsg(smb_t* smb, smbmsg_t* msg, int storage, long dupechk_hashes ,uint16_t xlat, const uchar* body, const uchar* tail); +SMBEXPORT int SMBCALL smb_addvote(smb_t* smb, smbmsg_t* msg, int storage); /* smballoc.c */ SMBEXPORT long SMBCALL smb_allochdr(smb_t* smb, ulong length);