From 68a90f319663097514a5af3806b756e667fe80c1 Mon Sep 17 00:00:00 2001 From: rswindell <> Date: Tue, 8 Nov 2016 20:17:12 +0000 Subject: [PATCH] Inspired by the U.S. presdential election: The beginnings of an SMB-based voting system - very experimental: The concept is that a "vote" message can be used to reply to: 1. A normal message, as either an upvote or a downvote, ala social media 2. A poll, polls can either allow a single choice answer or multiple answers Vote messages won't be visible as normal messages (e.g. when reading messages online) and SMB processing software (e.g. SBBSecho) should ignore these messages because they have no body text. Polls are going to need more work, but the idea is to have the poll question as a single (newly defined) hfield and the possible answers as dfields. --- src/sbbs3/atcodes.cpp | 14 ++++++++-- src/sbbs3/chksmb.c | 9 ++++--- src/sbbs3/fixsmb.c | 4 +-- src/sbbs3/readmsgs.cpp | 50 ++++++++++++++++++++++++++++++++---- src/sbbs3/sbbsdefs.h | 4 +-- src/smblib/smbadd.c | 58 ++++++++++++++++++++++++++++++++++++++++-- src/smblib/smbdefs.h | 32 +++++++++++++++++------ src/smblib/smblib.c | 47 ++++++++++++++++++++++++++++++++-- src/smblib/smblib.h | 4 +-- 9 files changed, 192 insertions(+), 30 deletions(-) diff --git a/src/sbbs3/atcodes.cpp b/src/sbbs3/atcodes.cpp index bb63c77587..5ba3bcc0cd 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 bb5f3ee545..a6e95edff3 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 fb7b3f3e3c..888448de6b 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 5bfa29fc37..07bce84f3e 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 5539d9b728..3c460cc6b8 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 56f6487132..561f7e4239 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 0912b2a6da..c1c28a40a3 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 327b6d7364..20fd0ee422 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 20942268a5..1f9f2242fc 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); -- GitLab