/* Add (flagadd=1) or remove (flagadd=0) userhost from the subscriber * database table. Comment is e.g. the subscriber from line or name. It * is added to the log. Event is the action type, e.g. "probe", * "manual", etc. The direction (sub/unsub) is inferred from * flagadd. Returns 1 on success, 0 on failure. If forcehash is >=0 it * is used in place of the calculated hash. This makes it possible to * add addresses with a hash that does not exist. forcehash has to be * 0..99. For unsubscribes, the address is only removed if forcehash * matches the actual hash. This way, ezmlm-manage can be prevented from * touching certain addresses that can only be removed by * ezmlm-unsub. Usually, this would be used for sublist addresses (to * avoid removal) and sublist aliases (to prevent users from subscribing * them (although the cookie mechanism would prevent the resulting * duplicate message from being distributed. */ int sub_sql_subscribe(struct subdbinfo *info, const char *table, const char *userhost, int flagadd, const char *comment, const char *event, int forcehash) { void *result; char *cpat; char szhash[3] = "00"; unsigned int j; unsigned char ch; int nparams; make_name(info,table?"_":0,table,0); /* lowercase and check address */ stralloc_copys(&addr,userhost); if (addr.len > 255) /* this is 401 in std ezmlm. 255 */ /* should be plenty! */ strerr_die2x(100,FATAL,MSG(ERR_ADDR_LONG)); j = byte_rchr(addr.s,addr.len,'@'); if (j == addr.len) strerr_die2x(100,FATAL,MSG(ERR_ADDR_AT)); cpat = addr.s + j; case_lowerb(cpat + 1,addr.len - j - 1); if (forcehash < 0) { stralloc_copy(&lcaddr,&addr); case_lowerb(lcaddr.s,j); /* make all-lc version of address */ ch = subhashsa(&lcaddr); } else ch = (forcehash % 100); szhash[0] = '0' + ch / 10; /* hash for sublist split */ szhash[1] = '0' + (ch % 10); if (flagadd) { /* FIXME: LOCK TABLES name WRITE */ stralloc_copys(&query,"SELECT address FROM "); stralloc_cat(&query,&name); stralloc_cats(&query," WHERE "); stralloc_cats(&query,sql_subscribe_select_where_defn); stralloc_copy(¶ms[0],&addr); result = sql_select(info,&query,1,params); if (sql_fetch_row(info,result,1,params)) { sql_free_result(info,result); /* FIXME: UNLOCK TABLES */ return 0; /* already subscribed */ } else { /* not there */ sql_free_result(info,result); stralloc_copys(&query,"INSERT INTO "); stralloc_cat(&query,&name); stralloc_cats(&query," (address,hash) VALUES "); stralloc_cats(&query,sql_subscribe_list_values_defn); stralloc_copy(¶ms[0],&addr); stralloc_copys(¶ms[1],szhash); sql_exec(info,&query,2,params); /* FIXME: UNLOCK TABLES */ } } else { /* unsub */ stralloc_copys(&query,"DELETE FROM "); stralloc_cat(&query,&name); stralloc_cats(&query," WHERE "); stralloc_copy(¶ms[0],&addr); if (forcehash >= 0) { stralloc_cats(&query,sql_subscribe_delete2_where_defn); stralloc_copys(¶ms[1],szhash); nparams = 2; } else { stralloc_cats(&query,sql_subscribe_delete1_where_defn); nparams = 1; } if (sql_exec(info,&query,1,params) == 0) return 0; /* address wasn't there*/ } /* log to subscriber log */ /* INSERT INTO t_slog (address,edir,etype,fromline) */ /* VALUES('address',{'+'|'-'},'etype','[comment]') */ stralloc_copys(&query,"INSERT INTO "); stralloc_cat(&query,&name); stralloc_cats(&query,"_slog (address,edir,etype,fromline) VALUES "); stralloc_cats(&query,sql_subscribe_slog_values_defn); stralloc_copy(¶ms[0],&addr); stralloc_copys(¶ms[1],flagadd?"+":"-"); /* edir */ stralloc_copyb(¶ms[2],event+1,!!*(event+1)); /* etype */ stralloc_copys(¶ms[3],comment && *comment ? comment : ""); /* from */ sql_exec(info,&query,4,params); /* log (ignore errors) */ stralloc_0(&addr); logaddr(table,event,addr.s,comment); /* also log to old log */ return 1; /* desired effect */ }
int std_subscribe(const char *dir, const char *subdir, const char *userhost, int flagadd, const char *comment, const char *event, int forcehash) /* add (flagadd=1) or remove (flagadd=0) userhost from the subscr. database */ /* dbname. Comment is e.g. the subscriber from line or name. It is added to */ /* the log. Event is the action type, e.g. "probe", "manual", etc. The */ /* direction (sub/unsub) is inferred from flagadd. Returns 1 on success, 0 */ /* on failure. If flagmysql is set and the file "sql" is found in the */ /* directory dbname, it is parsed and a mysql db is assumed. if forcehash is */ /* >=0 it is used in place of the calculated hash. This makes it possible to */ /* add addresses with a hash that does not exist. forcehash has to be 0..99. */ /* for unsubscribes, the address is only removed if forcehash matches the */ /* actual hash. This way, ezmlm-manage can be prevented from touching certain*/ /* addresses that can only be removed by ezmlm-unsub. Usually, this would be */ /* used for sublist addresses (to avoid removal) and sublist aliases (to */ /* prevent users from subscribing them (although the cookie mechanism would */ /* prevent the resulting duplicate message from being distributed. */ { int fdlock; unsigned int j; unsigned char ch,lcch; int match; int flagwasthere; if (userhost[str_chr(userhost,'\n')]) strerr_die2x(100,FATAL,ERR_ADDR_NL); if (!stralloc_copys(&addr,"T")) die_nomem(); if (!stralloc_cats(&addr,userhost)) die_nomem(); if (addr.len > 401) strerr_die2x(100,FATAL,ERR_ADDR_LONG); j = byte_rchr(addr.s,addr.len,'@'); if (j == addr.len) strerr_die2x(100,FATAL,ERR_ADDR_AT); case_lowerb(addr.s + j + 1,addr.len - j - 1); if (!stralloc_copy(&lcaddr,&addr)) die_nomem(); case_lowerb(lcaddr.s + 1,j - 1); /* make all-lc version of address */ if (forcehash >= 0 && forcehash <= 52) { ch = lcch = 64 + (unsigned char) forcehash; } else { ch = 64 + subhashsa(&addr); lcch = 64 + subhashsa(&lcaddr); } if (!stralloc_0(&addr)) die_nomem(); if (!stralloc_0(&lcaddr)) die_nomem(); std_makepath(&fn,dir,subdir,"/subscribers/",lcch); std_makepath(&fnlock,dir,subdir,"/lock",0); if (!stralloc_copyb(&fnnew,fn.s,fn.len-1)) die_nomem(); /* code later depends on fnnew = fn + 'n' */ if (!stralloc_cats(&fnnew,"n")) die_nomem(); if (!stralloc_0(&fnnew)) die_nomem(); fdlock = lockfile(fnlock.s); /* do lower case hashed version first */ fdnew = open_trunc(fnnew.s); if (fdnew == -1) die_write(); substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf)); flagwasthere = 0; fd = open_read(fn.s); if (fd == -1) { if (errno != error_noent) { close(fdnew); die_read(); } } else { substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf)); for (;;) { if (getln(&ss,&line,&match,'\0') == -1) { close(fd); close(fdnew); die_read(); } if (!match) break; if (line.len == addr.len) if (!case_diffb(line.s,line.len,addr.s)) { flagwasthere = 1; if (!flagadd) continue; } if (substdio_bput(&ssnew,line.s,line.len) == -1) { close(fd); close(fdnew); die_write(); } } close(fd); } if (flagadd && !flagwasthere) if (substdio_bput(&ssnew,addr.s,addr.len) == -1) { close(fdnew); die_write(); } if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(); } if (fsync(fdnew) == -1) { close(fdnew); die_write(); } close(fdnew); if (rename(fnnew.s,fn.s) == -1) strerr_die6sys(111,FATAL,ERR_MOVE,fnnew.s," to ",fn.s,": "); if ((ch == lcch) || flagwasthere) { close(fdlock); if (flagadd ^ flagwasthere) { if (!stralloc_0(&addr)) die_nomem(); logaddr(dir,subdir,event,addr.s+1,comment); return 1; } return 0; } /* If unsub and not found and hashed differ, OR */ /* sub and not found (so added with new hash) */ /* do the 'case-dependent' hash */ fn.s[fn.len - 2] = ch; fnnew.s[fnnew.len - 3] = ch; fdnew = open_trunc(fnnew.s); if (fdnew == -1) die_write(); substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf)); fd = open_read(fn.s); if (fd == -1) { if (errno != error_noent) { close(fdnew); die_read(); } } else { substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf)); for (;;) { if (getln(&ss,&line,&match,'\0') == -1) { close(fd); close(fdnew); die_read(); } if (!match) break; if (line.len == addr.len) if (!case_diffb(line.s,line.len,addr.s)) { flagwasthere = 1; continue; /* always want to remove from case-sensitive hash */ } if (substdio_bput(&ssnew,line.s,line.len) == -1) { close(fd); close(fdnew); die_write(); } } close(fd); } if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(); } if (fsync(fdnew) == -1) { close(fdnew); die_write(); } close(fdnew); if (rename(fnnew.s,fn.s) == -1) strerr_die6sys(111,FATAL,ERR_MOVE,fnnew.s," to ",fn.s,": "); close(fdlock); if (flagadd ^ flagwasthere) { if (!stralloc_0(&addr)) die_nomem(); logaddr(dir,subdir,event,addr.s+1,comment); return 1; } return 0; }
/* Add (flagadd=1) or remove (flagadd=0) userhost from the subscriber * database table. Comment is e.g. the subscriber from line or name. It * is added to the log. Event is the action type, e.g. "probe", * "manual", etc. The direction (sub/unsub) is inferred from * flagadd. Returns 1 on success, 0 on failure. If forcehash is >=0 it * is used in place of the calculated hash. This makes it possible to * add addresses with a hash that does not exist. forcehash has to be * 0..99. For unsubscribes, the address is only removed if forcehash * matches the actual hash. This way, ezmlm-manage can be prevented from * touching certain addresses that can only be removed by * ezmlm-unsub. Usually, this would be used for sublist addresses (to * avoid removal) and sublist aliases (to prevent users from subscribing * them (although the cookie mechanism would prevent the resulting * duplicate message from being distributed. */ static int _subscribe(struct subdbinfo *info, const char *table, const char *userhost, int flagadd, const char *comment, const char *event, int forcehash) { sqlite3_stmt *stmt; char *cpat; char szhash[3] = "00"; int res; unsigned int j; unsigned char ch; domain.len = 0; /* clear domain */ /* lowercase and check address */ if (!stralloc_copys(&addr,userhost)) die_nomem(); if (addr.len > 255) /* this is 401 in std ezmlm. 255 */ /* should be plenty! */ strerr_die2x(100,FATAL,MSG(ERR_ADDR_LONG)); j = byte_rchr(addr.s,addr.len,'@'); if (j == addr.len) strerr_die2x(100,FATAL,MSG(ERR_ADDR_AT)); cpat = addr.s + j; case_lowerb(cpat + 1,addr.len - j - 1); if (!stralloc_copy("ed, &addr)) die_nomem(); /* stored unescaped, so it should be ok if quoted.len is >255, as */ /* long as addr.len is not */ if (forcehash < 0) { if (!stralloc_copy(&lcaddr,&addr)) die_nomem(); case_lowerb(lcaddr.s,j); /* make all-lc version of address */ ch = subhashsa(&lcaddr); } else ch = (forcehash % 100); szhash[0] = '0' + ch / 10; /* hash for sublist split */ szhash[1] = '0' + (ch % 10); if (flagadd) { if (!stralloc_copys(&line,"SELECT address FROM ")) die_nomem(); if (!stralloc_cat_table(&line,info,table)) die_nomem(); if (!stralloc_cats(&line," WHERE address LIKE '")) die_nomem(); if (!stralloc_cat(&line,"ed)) die_nomem(); /* addr */ if (!stralloc_cats(&line,"'")) die_nomem(); if (!stralloc_0(&line)) die_nomem(); if ((stmt = _sqlquery(info, &line)) == NULL) strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); res = sqlite3_step(stmt); sqlite3_finalize(stmt); if (res == SQLITE_ROW) return 0; /* there */ else if (res != SQLITE_DONE) { strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); } else { /* not there */ if (!stralloc_copys(&line,"INSERT INTO ")) die_nomem(); if (!stralloc_cat_table(&line,info,table)) die_nomem(); if (!stralloc_cats(&line," (address,hash) VALUES ('")) die_nomem(); if (!stralloc_cat(&line,"ed)) die_nomem(); /* addr */ if (!stralloc_cats(&line,"',")) die_nomem(); if (!stralloc_cats(&line,szhash)) die_nomem(); /* hash */ if (!stralloc_cats(&line,")")) die_nomem(); if (!stralloc_0(&line)) die_nomem(); if ((stmt = _sqlquery(info, &line)) == NULL) strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); if (sqlite3_step(stmt) != SQLITE_DONE) strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); sqlite3_finalize(stmt); } } else { /* unsub */ if (!stralloc_copys(&line,"DELETE FROM ")) die_nomem(); if (!stralloc_cat_table(&line,info,table)) die_nomem(); if (!stralloc_cats(&line," WHERE address LIKE '")) die_nomem(); if (!stralloc_cat(&line,"ed)) die_nomem(); /* addr */ if (forcehash >= 0) { if (!stralloc_cats(&line,"' AND hash=")) die_nomem(); if (!stralloc_cats(&line,szhash)) die_nomem(); } else { if (!stralloc_cats(&line,"' AND hash BETWEEN 0 AND 52")) die_nomem(); } if (!stralloc_0(&line)) die_nomem(); if ((stmt = _sqlquery(info, &line)) == NULL) strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); if (sqlite3_step(stmt) != SQLITE_DONE) strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); sqlite3_finalize(stmt); if (sqlite3_changes((sqlite3*)info->conn) == 0) return 0; /* address wasn't there*/ } /* log to subscriber log */ /* INSERT INTO t_slog (address,edir,etype,fromline) */ /* VALUES('address',{'+'|'-'},'etype','[comment]') */ if (!stralloc_copys(&logline,"INSERT INTO ")) die_nomem(); if (!stralloc_cat_table(&logline,info,table)) die_nomem(); if (!stralloc_cats(&logline, "_slog (tai,address,edir,etype,fromline) VALUES (")) die_nomem(); if (!stralloc_catb(&logline,strnum,fmt_ulong(strnum,now()))) die_nomem(); if (!stralloc_cats(&logline,",'")) die_nomem(); if (!stralloc_cat(&logline,"ed)) die_nomem(); if (flagadd) { /* edir */ if (!stralloc_cats(&logline,"','+','")) die_nomem(); } else { if (!stralloc_cats(&logline,"','-','")) die_nomem(); } if (*(event + 1)) /* ezmlm-0.53 uses '' for ezmlm-manage's work */ if (!stralloc_catb(&logline,event+1,1)) die_nomem(); /* etype */ if (!stralloc_cats(&logline,"','")) die_nomem(); if (comment && *comment) { j = str_len(comment); if (!stralloc_copys("ed, comment)) die_nomem(); /* from */ if (!stralloc_cat(&logline,"ed)) die_nomem(); } if (!stralloc_cats(&logline,"')")) die_nomem(); if (!stralloc_0(&logline)) die_nomem(); if ((stmt = _sqlquery(info, &logline)) != NULL) { sqlite3_step(stmt); /* log (ignore errors) */ sqlite3_finalize(stmt); } if (!stralloc_0(&addr)) ; /* ignore errors */ logaddr(table,event,addr.s,comment); /* also log to old log */ return 1; /* desired effect */ }