int main( int argc, char* argv[] ) { int err = 0; int opt, bits=20, str_type = TYPE_WILD, ignore_boundary_flag; int case_flag = 0, check_flag = 0, db_flag = 0, core, res = HASHCASH_FAIL; int mint_flag = 0, purge_flag = 0, core_flag = 0; int time_width = 6, compress = 0, checked = 0; long anon_random = 0, anon_period = 0, purge_period = 0, time_period = 0; time_t real_time = time(0); /* now, in UTCTIME */ time_t now_time = real_time, token_time; int speed_flag = 0, utc_flag = 0, version_flag = 0, hdr_flag = 0; char* header = NULL; char* db_filename = "hashcash.sdb"; char* purge_resource = NULL; char* resource = NULL; char* ext = NULL; int purge_all = 0; long validity_period = 28*TIME_DAY; /* default validity period: 28 days */ long purge_validity_period = 28*TIME_DAY; /* same default */ long grace_period = 2*TIME_DAY; /* default grace period: 2 days */ long valid_for = HASHCASH_INVALID; char *new_token = NULL ; char token[MAX_TOK+1], token_utime[MAX_UTC+1], period[MAX_PERIOD+1]; double tries_taken = 0; void** compile = NULL; char* re_err = NULL; DB db; hashcash_callback callback = NULL; while ( (opt=getopt( argc, argv, "-a:b:cde:f:g:hij:klmnop:qr:sSt:uvwx:yz:CEMO:PSVXZ:")) >0 ) { switch( opt ) { case 'a': if ( !parse_period( optarg, &anon_period ) ) { usage( "error: -a invalid period arg" ); } break; case 'b': bits = atoi( optarg+1 ); if ( bits < 0 ) { usage( "error: -b invalid bits arg" ); } break; case 'C': case_flag = 1; break; case 'c': check_flag = 1; break; case 'd': db_flag = 1; break; case 'e': if ( !parse_period( optarg, &validity_period ) || validity_period < 0 ) { usage( "error: -e invalid validity period" ); } break; case 'E': str_type = TYPE_REGEXP; break; case 'f': db_filename = strdup( optarg ); break; case 'g': if ( optarg[0] == '-' ) { usage( "error: -g -ve grace period not valid" ); } if ( !parse_period( optarg, &grace_period ) ) { usage( "error: -g invalid grace period format" ); } break; case 'h': usage( "" ); break; case 'i': ignore_boundary_flag = 1; break; case 'j': purge_resource = optarg; break; case 'k': purge_all = 1; break; case 'm': mint_flag = 1; break; case 'M': str_type = TYPE_WILD; break; case 'O': core_flag = 1; core = atoi( optarg ); res = hashcash_use_core( core ); if ( res < 1 ) { usage( res == -1 ? "error: -O no such core\n" : "error: -O core does not work on this platform" ); } break; case 'p': purge_flag = 1; if ( strcmp( optarg, "now" ) == 0 ) { purge_period = 0; } else if ( !parse_period( optarg, &purge_period ) || purge_period < 0 ) { usage( "error: -p invalid purge interval" ); } break; case 'P': callback = progress_callback; break; case 1: case 'r': resource = optarg; break; case 's': speed_flag = 1; break; case 'S': str_type = TYPE_STR; break; case 't': if ( optarg[0] == '-' || optarg[0] == '+' ) { if ( !parse_period( optarg, &time_period ) ) { usage( "error: -t invalid relative time format" ); } now_time += time_period; } else { now_time = hashcash_from_utctimestr( optarg, utc_flag ); if ( now_time == (time_t)-1 ) { usage( "error: -t invalid time format" ); } } break; case 'u': utc_flag = 1; break; case 'V': version_flag = 1; break; case 'x': ext = strdup( optarg ); break; case 'X': hdr_flag = 1; sstrncpy( header, "X-Hashcash: ", MAX_HDR ); break; case 'z': time_width = atoi( optarg ); if ( time_width != 6 && time_width != 10 && time_width != 12 ) { usage( "error: -z invalid time width: must be 6, 10 or 12" ); } break; case 'Z': compress = atoi( optarg ); break; case '?': fprintf( stderr, "error: unrecognized option -%c", optopt ); usage( "" ); break; case ':': fprintf( stderr, "error: option -%c missing argument", optopt ); usage( "" ); break; default: usage( "error with argument processing" ); break; } } if ( version_flag ) { fprintf( stdout, "%s\n", HASHCASH_VERSION_STRING ); exit( EXIT_FAILURE ); } if ( speed_flag ) { if ( core_flag ) { hashcash_benchtest( 3, core ); } else { hashcash_benchtest( 3, -1 ); } exit( EXIT_FAILURE ); } if ( mint_flag ) { err = hashcash_mint( now_time, time_width, resource, bits, anon_period, &new_token, &anon_random, &tries_taken, ext, compress, callback, NULL ); } else if ( purge_flag ) { if ( !hashcash_db_open( &db, db_filename, &err ) ) { die(err); } if ( !hashcash_db_purge( &db, purge_resource, str_type, case_flag, validity_period, grace_period, purge_all, purge_period, now_time, &err ) ) { die(err); } if ( !hashcash_db_close( &db, &err ) ) { die(err); } } else if ( check_flag ) { valid_for = hashcash_check( token, case_flag, resource, compile, &re_err, str_type, now_time, validity_period, grace_period, bits, &token_time ); if ( valid_for < 0 ) { switch ( valid_for ) { case HASHCASH_INSUFFICIENT_BITS: fprintf( stderr, "no match: token has insufficient bits\n" ); break; case HASHCASH_VALID_IN_FUTURE: fprintf( stderr, "no match: valid in future\n" ); break; case HASHCASH_EXPIRED: fprintf( stderr, "no match: token expired\n" ); break; case HASHCASH_WRONG_RESOURCE: fprintf( stderr, "no match: wrong resource\n" ); break; case HASHCASH_REGEXP_ERROR: fprintf( stderr, "regexp error: " ); die_msg( re_err ); break; default: die_msg( "internal error" ); break; } exit( EXIT_FAILURE ); } checked = 1; } if ( db_flag && check_flag && checked ) { if ( !hashcash_db_open( &db, db_filename, &err ) ) { die(err); } if ( hashcash_db_in( &db, token, token_utime, &err ) ) { if ( err ) { die(err); } fprintf( stderr, "stamp: double spent\n" ); } sprintf( period, "%ld", (long)validity_period ); if ( !hashcash_db_add( &db, token, period, &err ) ) { die(err); } if ( !hashcash_db_close( &db, &err ) ) { die(err); } } exit( EXIT_SUCCESS ); }
void main(int argc,char **argv) { char *dir; int fdlock; char *sender; int match; int flaginheader; int flagmodpost; int flagremote; const char *pmod; const char *err; int opt; unsigned int i; char szchar[2] = "-"; int child; (void) umask(022); sig_pipeignore(); if (!stralloc_copys(&sendopt,"-")) die_nomem(); while ((opt = getopt(argc,argv,"bBcCmMpPrRsSvVyY")) != opteof) switch(opt) { case 'b': flagbody = 1; break; case 'B': flagbody = 0; break; case 'm': flagmime = 1; break; case 'M': flagmime = 0; break; case 'p': flagpublic = 1; break; /* anyone can post (still moderated)*/ case 'P': flagpublic = 0; break; /* only moderators can post */ case 's': flagself = 1; break; /* modpost and DIR/mod diff fxns */ case 'S': flagself = 0; break; /* same fxn */ case 'y': flagconfirm = 1; break; /* force post confirmation */ case 'Y': flagconfirm = 0; break; /* disable post confirmation */ case 'c': /* ezmlm-send flags */ case 'C': case 'r': case 'R': szchar[0] = (char) opt & 0xff; if (!stralloc_append(&sendopt,szchar)) die_nomem(); break; case 'v': case 'V': strerr_die2x(0,"ezmlm-store version: ",auto_version); default: die_usage(); } sender = env_get("SENDER"); if (sender) { if (!*sender || str_equal(sender,"#@[]")) strerr_die2x(100,FATAL,ERR_BOUNCE); } startup(dir = argv[optind]); load_config(dir); if (flagconfirm == -1) flagconfirm = getconf_line(&confirmpost,"confirmpost",0,dir); else getconf_line(&confirmpost,"confirmpost",0,dir); flagmodpost = getconf_line(&moderators,"modpost",0,dir); flagremote = getconf_line(&line,"remote",0,dir); if (!flagmodpost && !flagconfirm) { /* not msg-mod. Pipe to ezmlm-send */ if ((child = wrap_fork()) == 0) wrap_execbin("/ezmlm-send", &sendopt, dir); /* parent */ wrap_exitcode(child); } if (!moderators.len || !(moderators.s[0] == '/')) { if (!stralloc_copys(&moderators,dir)) die_nomem(); if (!stralloc_cats(&moderators,"/mod")) die_nomem(); } if (!stralloc_0(&moderators)) die_nomem(); if (sender) { pmod = issub(moderators.s,0,sender); closesub(); /* sender = moderator? */ } else pmod = 0; if (!pmod && !flagpublic) strerr_die2x(100,FATAL,ERR_NO_POST); fdlock = lockfile("mod/lock"); if (!stralloc_copys(&mydtline, flagconfirm ? "Delivered-To: confirm to " : "Delivered-To: moderator for ")) die_nomem(); if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem(); if (!stralloc_append(&mydtline,"@")) die_nomem(); if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem(); if (!stralloc_cats(&mydtline,"\n")) die_nomem(); if (!stralloc_copys(&returnpath,"Return-Path: <")) die_nomem(); if (sender) { if (!stralloc_cats(&returnpath,sender)) die_nomem(); for (i = 14; i < returnpath.len;++i) if (returnpath.s[i] == '\n' || !returnpath.s[i] ) returnpath.s[i] = '_'; /* NUL and '\n' are bad, but we don't quote since this is */ /* only for ezmlm-moderate, NOT for SMTP */ } if (!stralloc_cats(&returnpath,">\n")) die_nomem(); pid = getpid(); /* unique file name */ for (i = 0;;++i) /* got lock - nobody else can add files */ { when = now(); /* when is also used later for date! */ if (!stralloc_copys(&fnmsg, flagconfirm?"mod/unconfirmed/":"mod/pending/")) die_nomem(); if (!stralloc_copyb(&fnbase,strnum,fmt_ulong(strnum,when))) die_nomem(); if (!stralloc_append(&fnbase,".")) die_nomem(); if (!stralloc_catb(&fnbase,strnum,fmt_ulong(strnum,pid))) die_nomem(); if (!stralloc_cat(&fnmsg,&fnbase)) die_nomem(); if (!stralloc_0(&fnmsg)) die_nomem(); if (stat(fnmsg.s,&st) == -1) if (errno == error_noent) break; /* really should never get to this point */ if (i == 2) strerr_die2x(111,FATAL,ERR_UNIQUE); sleep(2); } if (!stralloc_copys(&action,"-")) die_nomem(); if (!stralloc_cats(&action,flagconfirm?ACTION_DISCARD:ACTION_REJECT)) die_nomem(); if (!stralloc_cat(&action,&fnbase)) die_nomem(); if (!stralloc_0(&action)) die_nomem(); makeacthash(&action); if (!quote("ed,&outlocal)) die_nomem(); if (!stralloc_copy(&reject,"ed)) die_nomem(); if (!stralloc_cat(&reject,&action)) die_nomem(); if (!stralloc_0(&reject)) die_nomem(); if (!stralloc_copys(&action,"-")) die_nomem(); if (!stralloc_cats(&action,flagconfirm?ACTION_CONFIRM:ACTION_ACCEPT)) die_nomem(); if (!stralloc_cat(&action,&fnbase)) die_nomem(); if (!stralloc_0(&action)) die_nomem(); makeacthash(&action); if (!stralloc_copy(&accept,"ed)) die_nomem(); if (!stralloc_cat(&accept,&action)) die_nomem(); if (!stralloc_0(&accept)) die_nomem(); set_cptarget(accept.s); /* for copy () */ set_cpconfirm(reject.s,quoted.len); fdmsg = open_trunc(fnmsg.s); if (fdmsg == -1) strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnmsg.s,": "); substdio_fdbuf(&ssmsg,write,fdmsg,msgbuf,sizeof(msgbuf)); if (qmail_open(&qq, (stralloc *) 0) == -1) /* Open mailer */ strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE); hdr_add2("Mailing-List: ",mailinglist.s,mailinglist.len); if (listid.len > 0) hdr_add2("List-ID: ",listid.s,listid.len); hdr_datemsgid(when); if (flagconfirm) hdr_from("-owner"); else hdr_add2s("From: ",reject.s); hdr_add2s("Reply-To: ",accept.s); if (!flagconfirm && !pmod && flagremote) { /* if remote admin add -allow- address */ qmail_puts(&qq,"Cc: "); /* for ezmlm-gate users */ strnum[fmt_ulong(strnum,(unsigned long) when)] = 0; cookie(hash,key.s,key.len-FLD_ALLOW,strnum,sender,"t"); if (!stralloc_copy(&line,&outlocal)) die_nomem(); if (!stralloc_cats(&line,"-allow-tc.")) die_nomem(); if (!stralloc_cats(&line,strnum)) die_nomem(); if (!stralloc_append(&line,".")) die_nomem(); if (!stralloc_catb(&line,hash,COOKIE)) die_nomem(); if (!stralloc_append(&line,"-")) die_nomem(); i = str_rchr(sender,'@'); if (!stralloc_catb(&line,sender,i)) die_nomem(); if (sender[i]) { if (!stralloc_append(&line,"=")) die_nomem(); if (!stralloc_cats(&line,sender + i + 1)) die_nomem(); } qmail_put(&qq,line.s,line.len); qmail_puts(&qq,"@"); qmail_put(&qq,outhost.s,outhost.len); qmail_puts(&qq,"\n"); } qmail_puts(&qq,"To: <"); if (flagconfirm) { if (sender) qmail_puts(&qq, sender); } else { if (!quote("ed,&outlocal)) die_nomem(); qmail_put(&qq,quoted.s,quoted.len); qmail_puts(&qq,"-moderators@"); qmail_put(&qq,outhost.s,outhost.len); } qmail_puts(&qq,">\n"); /* FIXME: Drop the custom subject hack and use hdr_listsubject1 */ if (!stralloc_copys(&subject,"Subject: ")) die_nomem(); if (flagconfirm) { if (confirmpost.len) { if (!stralloc_cat(&subject,&confirmpost)) die_nomem(); if (!stralloc_cats(&subject," ")) die_nomem(); } else { if (!stralloc_cats(&subject,TXT_CONFIRM_POST)) die_nomem(); } } else { if (!stralloc_cats(&subject,TXT_MODERATE)) die_nomem(); } if (!quote("ed,&outlocal)) die_nomem(); if (!stralloc_cat(&subject,"ed)) die_nomem(); if (!stralloc_append(&subject,"@")) die_nomem(); if (!stralloc_cat(&subject,&outhost)) die_nomem(); if (flagmime) { hdr_mime(CTYPE_MULTIPART); qmail_put(&qq,subject.s,subject.len); hdr_boundary(0); hdr_ctype(CTYPE_TEXT); hdr_transferenc(); } else { qmail_put(&qq,subject.s,subject.len); qmail_puts(&qq,"\n\n"); } copy(&qq,flagconfirm?"text/post-confirm":"text/mod-request",flagcd); if (flagcd == 'B') { encodeB("",0,&line,2); qmail_put(&qq,line.s,line.len); } if (substdio_put(&ssmsg,returnpath.s,returnpath.len) == -1) die_msg(); if (substdio_put(&ssmsg,mydtline.s,mydtline.len) == -1) die_msg(); substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf)); if (flagmime) { hdr_boundary(0); hdr_ctype(CTYPE_MESSAGE); qmail_puts(&qq, "\n"); } qmail_put(&qq,returnpath.s,returnpath.len); qmail_put(&qq,mydtline.s,mydtline.len); flaginheader = 1; for (;;) { if (getln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,ERR_READ_INPUT); if (!match) break; if (line.len == 1) flaginheader = 0; if (flaginheader) { if ((line.len == mydtline.len) && !byte_diff(line.s,line.len,mydtline.s)) { close(fdmsg); /* be nice - clean up */ unlink(fnmsg.s); strerr_die2x(100,FATAL,ERR_LOOPING); } if (case_startb(line.s,line.len,"mailing-list:")) { close(fdmsg); /* be nice - clean up */ unlink(fnmsg.s); strerr_die2x(100,FATAL,ERR_MAILING_LIST); } } if (flagbody || flaginheader) /* skip body if !flagbody */ qmail_put(&qq,line.s,line.len); if (substdio_put(&ssmsg,line.s,line.len) == -1) die_msg(); } if (flagmime) hdr_boundary(1); /* close archive before qmail. Loss of qmail will result in re-run, and */ /* worst case this results in a duplicate msg sitting orphaned until it's */ /* cleaned out. */ if (substdio_flush(&ssmsg) == -1) die_msg(); if (fsync(fdmsg) == -1) die_msg(); if (fchmod(fdmsg,MODE_MOD_MSG | 0700) == -1) die_msg(); if (close(fdmsg) == -1) die_msg(); /* NFS stupidity */ close(fdlock); if (flagconfirm) { qmail_from(&qq,reject.s); /* envelope sender */ } else { if (!stralloc_copy(&line,&outlocal)) die_nomem(); if (!stralloc_cats(&line,"-return-@")) die_nomem(); if (!stralloc_cat(&line,&outhost)) die_nomem(); if (!stralloc_0(&line)) die_nomem(); qmail_from(&qq,line.s); /* envelope sender */ } if (flagconfirm) /* to sender */ qmail_to(&qq,sender); else if (pmod) /* to moderator only */ qmail_to(&qq,pmod); else { if (flagself) { /* to all moderators */ if (!stralloc_copys(&moderators,dir)) die_nomem(); if (!stralloc_cats(&moderators,"/mod")) die_nomem(); if (!stralloc_0(&moderators)) die_nomem(); } putsubs(moderators.s,0,0,52,subto,1); } if (*(err = qmail_close(&qq)) == '\0') { strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; strerr_die2x(0,"ezmlm-store: info: qp ",strnum); } else strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err+1); }