From 9f9559473a83f48febecf4b1782953e28443219d Mon Sep 17 00:00:00 2001 From: Bruce Guenter Date: Fri, 19 Nov 2010 17:22:47 -0600 Subject: [PATCH] sub-*sql: Merge subscribe implementations into a common function --- sub-mysql.c | 181 +++--------------------------------------- sub-pgsql.c | 193 ++++----------------------------------------- sub-sqlite3.c | 214 ++++++-------------------------------------------- sub_sql.c | 121 +++++++++++++++++++++++++++- sub_sql.h | 20 ++++- 5 files changed, 185 insertions(+), 544 deletions(-) diff --git a/sub-mysql.c b/sub-mysql.c index 04da629..d47382c 100644 --- a/sub-mysql.c +++ b/sub-mysql.c @@ -35,24 +35,7 @@ #include #include -static stralloc addr = {0}; -static stralloc domain = {0}; -static stralloc lcaddr = {0}; static stralloc line = {0}; -static stralloc logline = {0}; -static stralloc quoted = {0}; - -static int stralloc_cat_table(stralloc *s, - const struct subdbinfo *info, - const char *table) -{ - if (!stralloc_cats(s,info->base_table)) return 0; - if (table) { - if (!stralloc_append(s,"_")) return 0; - if (!stralloc_cats(s,table)) return 0; - } - return 1; -} /* close connection to SQL server, if open */ static void _closesub(struct subdbinfo *info) @@ -75,153 +58,6 @@ static const char *_opensub(struct subdbinfo *info) return (char *) 0; } -static void safe_query(struct subdbinfo *info,const stralloc* query) -{ - if (mysql_real_query((MYSQL*)info->conn,query->s,query->len)) - strerr_die2x(111,FATAL,mysql_error((MYSQL*)info->conn)); -} - -/* 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) -{ - MYSQL_RES *result; - MYSQL_ROW row; - char *cpat; - char szhash[3] = "00"; - - 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_ready("ed,2 * addr.len + 1)) die_nomem(); - quoted.len = mysql_escape_string(quoted.s,addr.s,addr.len); - /* 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,"LOCK TABLES ")) die_nomem(); - if (!stralloc_cat_table(&line,info,table)) die_nomem(); - if (!stralloc_cats(&line," WRITE")) die_nomem(); - safe_query(info,&line); - if (!stralloc_copys(&line,"SELECT address FROM ")) die_nomem(); - if (!stralloc_cat_table(&line,info,table)) die_nomem(); - if (!stralloc_cats(&line," WHERE address='")) die_nomem(); - if (!stralloc_cat(&line,"ed)) die_nomem(); /* addr */ - if (!stralloc_cats(&line,"'")) die_nomem(); - safe_query(info,&line); - if (!(result = mysql_use_result((MYSQL*)info->conn))) - strerr_die2x(111,FATAL,mysql_error((MYSQL*)info->conn)); - if ((row = mysql_fetch_row(result))) { /* there */ - while (mysql_fetch_row(result)); /* use'm up */ - mysql_free_result(result); - if (mysql_query((MYSQL*)info->conn,"UNLOCK TABLES")) - strerr_die2x(111,FATAL,mysql_error((MYSQL*)info->conn)); - return 0; /* there */ - } else { /* not there */ - mysql_free_result(result); - if (mysql_errno((MYSQL*)info->conn)) /* or ERROR */ - strerr_die2x(111,FATAL,mysql_error((MYSQL*)info->conn)); - 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(); - safe_query(info,&line); - if (mysql_query((MYSQL*)info->conn,"UNLOCK TABLES")) - strerr_die2x(111,FATAL,mysql_error((MYSQL*)info->conn)); - } - } 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='")) 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(); - } - safe_query(info,&line); - if (mysql_affected_rows((MYSQL*)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 (address,edir,etype,fromline) VALUES ('")) 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_ready("ed,2 * j + 1)) die_nomem(); - quoted.len = mysql_escape_string(quoted.s,comment,j); /* from */ - if (!stralloc_cat(&logline,"ed)) die_nomem(); - } - if (!stralloc_cats(&logline,"')")) die_nomem(); - - if (mysql_real_query((MYSQL*)info->conn,logline.s,logline.len)) - ; /* log (ignore errors) */ - if (!stralloc_0(&addr)) - ; /* ignore errors */ - logaddr(table,event,addr.s,comment); /* also log to old log */ - return 1; /* desired effect */ -} - static void die_sqlerror(struct subdbinfo *info) { strerr_die2x(111,FATAL,mysql_error((MYSQL*)info->conn)); @@ -251,10 +87,10 @@ MYSQL_STMT *_prepbind(struct subdbinfo *info, return stmt; } -int sql_insert(struct subdbinfo *info, - struct stralloc *q, - unsigned int nparams, - struct stralloc *params) +int sql_exec(struct subdbinfo *info, + struct stralloc *q, + unsigned int nparams, + struct stralloc *params) { int rows; MYSQL_STMT *stmt; @@ -403,6 +239,13 @@ const char sql_putsubs_where_defn[] = "hash BETWEEN ? AND ?"; const char sql_searchlog_select_defn[] = "UNIX_TIMESTAMP(tai), CONCAT(edir,etype,' ',address,' ',fromline)"; const char sql_searchlog_where_defn[] = "fromline LIKE concat('%',?,'%') OR address LIKE concat('%',?,'%')"; +/* Definition of clauses for subscribe queries */ +const char sql_subscribe_select_where_defn[] = "address=?"; +const char sql_subscribe_list_values_defn[] = "(?,?)"; +const char sql_subscribe_delete1_where_defn[] = "address=? and hash BETWEEN 0 AND 52"; +const char sql_subscribe_delete2_where_defn[] = "address=? and hash=?"; +const char sql_subscribe_slog_values_defn[] = "(?,?,?,?)"; + /* Definition of VALUES clause for insert in tagmsg */ const char sql_tagmsg_values_defn[] = "(?,NOW(),?,?,?)"; @@ -445,6 +288,6 @@ struct sub_plugin sub_plugin = { sub_sql_putsubs, sub_sql_rmtab, sub_sql_searchlog, - _subscribe, + sub_sql_subscribe, sub_sql_tagmsg, }; diff --git a/sub-pgsql.c b/sub-pgsql.c index 18de113..a2b0347 100644 --- a/sub-pgsql.c +++ b/sub-pgsql.c @@ -36,12 +36,7 @@ #include static char strnum[FMT_ULONG]; -static stralloc addr = {0}; -static stralloc domain = {0}; -static stralloc lcaddr = {0}; static stralloc line = {0}; -static stralloc logline = {0}; -static stralloc quoted = {0}; struct _result { @@ -73,18 +68,6 @@ static const char *_opensub(struct subdbinfo *info) return (char *) 0; } -static int stralloc_cat_table(stralloc *s, - const struct subdbinfo *info, - const char *table) -{ - if (!stralloc_cats(s,info->base_table)) return 0; - if (table) { - if (!stralloc_append(s,"_")) return 0; - if (!stralloc_cats(s,table)) return 0; - } - return 1; -} - /* close connection to SQL server, if open */ static void _closesub(struct subdbinfo *info) { @@ -93,158 +76,6 @@ static void _closesub(struct subdbinfo *info) info->conn = 0; /* Destroy pointer */ } -/* 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) -{ - PGresult *result; - char *cpat; - char szhash[3] = "00"; - 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_ready("ed,2 * addr.len + 1)) die_nomem(); - quoted.len = PQescapeString(quoted.s,addr.s,addr.len); - /* stored unescaped, so it should be ok if quoted.len is >255, as */ - /* long as addr.len is not */ - - if (!stralloc_ready("ed,2 * addr.len + 1)) die_nomem(); - quoted.len = PQescapeString(quoted.s,addr.s,addr.len); - /* 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 ~* '^")) die_nomem(); - if (!stralloc_cat(&line,"ed)) die_nomem(); /* addr */ - if (!stralloc_cats(&line,"$'")) die_nomem(); - if (!stralloc_0(&line)) die_nomem(); - result = PQexec((PGconn*)info->conn,line.s); - if (result == NULL) - strerr_die2x(111,FATAL,PQerrorMessage((PGconn*)info->conn)); - if (PQresultStatus(result) != PGRES_TUPLES_OK) - strerr_die2x(111,FATAL,PQresultErrorMessage(result)); - - if (PQntuples(result)>0) { /* there */ - PQclear(result); - return 0; /* there */ - } else { /* not there */ - PQclear(result); - 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(); - result = PQexec((PGconn*)info->conn,line.s); - if (result == NULL) - strerr_die2x(111,FATAL,PQerrorMessage((PGconn*)info->conn)); - if (PQresultStatus(result) != PGRES_COMMAND_OK) - strerr_die2x(111,FATAL,PQresultErrorMessage(result)); - } - } 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 ~* '^")) 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(); - result = PQexec((PGconn*)info->conn,line.s); - if (result == NULL) - strerr_die2x(111,FATAL,PQerrorMessage((PGconn*)info->conn)); - if (PQresultStatus(result) != PGRES_COMMAND_OK) - strerr_die2x(111,FATAL,PQresultErrorMessage(result)); - if (atoi(PQcmdTuples(result))<1) - return 0; /* address wasn't there*/ - PQclear(result); - } - - /* 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 (address,edir,etype,fromline) VALUES ('")) 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_ready("ed,2 * j + 1)) die_nomem(); - quoted.len = PQescapeString(quoted.s,comment,j); /* from */ - if (!stralloc_cat(&logline,"ed)) die_nomem(); - } - if (!stralloc_cats(&logline,"')")) die_nomem(); - - if (!stralloc_0(&logline)) die_nomem(); - result = PQexec((PGconn*)info->conn,logline.s); /* log (ignore errors) */ - PQclear(result); - - if (!stralloc_0(&addr)) - ; /* ignore errors */ - logaddr(table,event,addr.s,comment); /* also log to old log */ - return 1; /* desired effect */ -} - static void die_sqlerror(struct subdbinfo *info) { strerr_die2x(111,FATAL,PQerrorMessage((PGconn*)info->conn)); @@ -278,19 +109,20 @@ PGresult *_execute(struct subdbinfo *info, return result; } -int sql_insert(struct subdbinfo *info, - struct stralloc *q, - unsigned int nparams, - struct stralloc *params) +int sql_exec(struct subdbinfo *info, + struct stralloc *q, + unsigned int nparams, + struct stralloc *params) { PGresult *result; - int rows; + unsigned long rows; const char *err; result = _execute(info,q,nparams,params); switch (PQresultStatus(result)) { case PGRES_COMMAND_OK: - rows = 1; + err = PQcmdTuples(result); + (void)scan_ulong(err,&rows); break; default: /* This is ugly, but I can't find another good way of doing this */ @@ -432,7 +264,7 @@ const char sql_checktag_listno_where_defn[] = "listno=$1 AND msgnum=$2 AND done const char sql_checktag_msgnum_where_defn[] = "msgnum=$1 AND cookie=$2"; /* Definition of WHERE clause for selecting addresses in issub */ -const char sql_issub_where_defn[] = "address ~* ('^' || $1 || '$')::text"; +const char sql_issub_where_defn[] = "address ~* ('^' || $1 || '$')"; /* Definition of VALUES clause for insert in logmsg */ const char sql_logmsg_values_defn[] = "($1,$2,$3,$4)"; @@ -444,6 +276,13 @@ const char sql_putsubs_where_defn[] = "hash BETWEEN $1 AND $2"; const char sql_searchlog_select_defn[] = "extract(epoch from tai),address||' '||edir||etype||' '||fromline"; const char sql_searchlog_where_defn[] = "fromline LIKE concat('%',$1,'%') OR address LIKE concat('%',$1,'%')"; +/* Definition of clauses for subscribe queries */ +const char sql_subscribe_select_where_defn[] = "address=$1"; +const char sql_subscribe_list_values_defn[] = "($1,$2)"; +const char sql_subscribe_delete1_where_defn[] = "address ~* ('^' || $1 || '$') and hash BETWEEN 0 AND 52"; +const char sql_subscribe_delete2_where_defn[] = "address ~* ('^' || $1 || '$') and hash=$2"; +const char sql_subscribe_slog_values_defn[] = "($1,$2,$3,$4)"; + /* Definition of VALUES clause for insert in tagmsg */ const char sql_tagmsg_values_defn[] = "($1,NOW(),$2,$3,$4)"; @@ -475,6 +314,6 @@ struct sub_plugin sub_plugin = { sub_sql_putsubs, sub_sql_rmtab, sub_sql_searchlog, - _subscribe, + sub_sql_subscribe, sub_sql_tagmsg, }; diff --git a/sub-sqlite3.c b/sub-sqlite3.c index 5dcd769..1fef519 100644 --- a/sub-sqlite3.c +++ b/sub-sqlite3.c @@ -32,25 +32,7 @@ #include #include -static char strnum[FMT_ULONG]; -static stralloc addr = {0}; -static stralloc domain = {0}; -static stralloc lcaddr = {0}; static stralloc line = {0}; -static stralloc logline = {0}; -static stralloc quoted = {0}; - -static int stralloc_cat_table(stralloc *s, - const struct subdbinfo *info, - const char *table) -{ - if (!stralloc_cats(s,info->base_table)) return 0; - if (table) { - if (!stralloc_append(s,"_")) return 0; - if (!stralloc_cats(s,table)) return 0; - } - return 1; -} static sqlite3_stmt *_sqlquery(const struct subdbinfo *info, const stralloc *str) { @@ -83,190 +65,31 @@ static const char *_opensub(struct subdbinfo *info) return (char *) 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 */ -} - static void die_sqlerror(struct subdbinfo *info) { strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); } -int sql_insert(struct subdbinfo *info, - struct stralloc *q, - unsigned int nparams, - struct stralloc *params) +int sql_exec(struct subdbinfo *info, + struct stralloc *q, + unsigned int nparams, + struct stralloc *params) { sqlite3_stmt *stmt; - int res; + int rows = 0; stmt = sql_select(info,q,nparams,params); - res = sqlite3_step(stmt); - sqlite3_finalize(stmt); - - if (res != SQLITE_DONE) - { - if (res == SQLITE_CONSTRAINT) /* duplicated key */ - return 0; + switch (sqlite3_step(stmt)) { + case SQLITE_DONE: + rows = sqlite3_changes((sqlite3*)info->conn); + break; + case SQLITE_CONSTRAINT: /* duplicated key on insert */ + break; + default: die_sqlerror(info); } - return 1; + sqlite3_finalize(stmt); + return rows; } void *sql_select(struct subdbinfo *info, @@ -413,6 +236,13 @@ const char sql_putsubs_where_defn[] = "hash BETWEEN ? AND ?"; const char sql_searchlog_select_defn[] = "tai, edir||etype||' '||address||' '||fromline"; const char sql_searchlog_where_defn[] = "fromline LIKE concat('%',?,'%') OR address LIKE concat('%',?,'%')"; +/* Definition of clauses for subscribe queries */ +const char sql_subscribe_select_where_defn[] = "address=?"; +const char sql_subscribe_list_values_defn[] = "(?,?)"; +const char sql_subscribe_delete1_where_defn[] = "address LIKE ? and hash BETWEEN 0 AND 52"; +const char sql_subscribe_delete2_where_defn[] = "address LIKE ? and hash=?"; +const char sql_subscribe_slog_values_defn[] = "(?,?,?,?)"; + /* Definition of VALUES clause for insert in tagmsg */ const char sql_tagmsg_values_defn[] = "(?,'now',?,?,?)"; @@ -448,6 +278,6 @@ struct sub_plugin sub_plugin = { sub_sql_putsubs, sub_sql_rmtab, sub_sql_searchlog, - _subscribe, + sub_sql_subscribe, sub_sql_tagmsg, }; diff --git a/sub_sql.c b/sub_sql.c index a47e89a..e522f0a 100644 --- a/sub_sql.c +++ b/sub_sql.c @@ -5,17 +5,20 @@ #include "datetime.h" #include "die.h" #include "fmt.h" +#include "log.h" #include "messages.h" #include "scan.h" #include "stralloc.h" #include "strerr.h" #include "sub_sql.h" #include "subdb.h" +#include "subhash.h" static stralloc addr; static stralloc name; static stralloc query; static stralloc params[4]; +static stralloc lcaddr; static char strnum[FMT_ULONG]; static void die_write(void) @@ -157,7 +160,7 @@ const char *sub_sql_logmsg(struct subdbinfo *info, s[fmt_uint(s,done)] = 0; if (!stralloc_copys(¶ms[3],s)) die_nomem(); - sql_insert(info,&query,4,params); /* ignore dups */ + sql_exec(info,&query,4,params); /* ignore dups */ return 0; } @@ -250,6 +253,120 @@ void sub_sql_searchlog(struct subdbinfo *info, sql_free_result(info,result); } +/* 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 */ + 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 (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) { + /* FIXME: LOCK TABLES name WRITE */ + if (!stralloc_copys(&query,"SELECT address FROM ")) die_nomem(); + if (!stralloc_cat(&query,&name)) die_nomem(); + if (!stralloc_cats(&query," WHERE ")) die_nomem(); + if (!stralloc_cats(&query,sql_subscribe_select_where_defn)) die_nomem(); + if (!stralloc_copy(¶ms[0],&addr)) die_nomem(); + 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); + + if (!stralloc_copys(&query,"INSERT INTO ")) die_nomem(); + if (!stralloc_cat(&query,&name)) die_nomem(); + if (!stralloc_cats(&query," (address,hash) VALUES ")) die_nomem(); + if (!stralloc_cats(&query,sql_subscribe_list_values_defn)) die_nomem(); + if (!stralloc_copy(¶ms[0],&addr)) die_nomem(); + if (!stralloc_copys(¶ms[1],szhash)) die_nomem(); + sql_exec(info,&query,2,params); + /* FIXME: UNLOCK TABLES */ + } + } else { /* unsub */ + if (!stralloc_copys(&query,"DELETE FROM ")) die_nomem(); + if (!stralloc_cat(&query,&name)) die_nomem(); + if (!stralloc_cats(&query," WHERE ")) die_nomem(); + if (!stralloc_copy(¶ms[0],&addr)) die_nomem(); + if (forcehash >= 0) { + if (!stralloc_cats(&query,sql_subscribe_delete2_where_defn)) die_nomem(); + if (!stralloc_copys(¶ms[1],szhash)) die_nomem(); + nparams = 2; + } + else { + if (!stralloc_cats(&query,sql_subscribe_delete1_where_defn)) die_nomem(); + 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]') */ + + if (!stralloc_copys(&query,"INSERT INTO ")) die_nomem(); + if (!stralloc_cat(&query,&name)) die_nomem(); + if (!stralloc_cats(&query,"_slog (address,edir,etype,fromline) VALUES ")) die_nomem(); + if (!stralloc_cats(&query,sql_subscribe_slog_values_defn)) die_nomem(); + if (!stralloc_copy(¶ms[0],&addr)) die_nomem(); + if (!stralloc_copys(¶ms[1],flagadd?"+":"-")) die_nomem(); /* edir */ + if (!stralloc_copyb(¶ms[2],event+1,!!*(event+1))) die_nomem(); /* etype */ + if (!stralloc_copys(¶ms[3],comment && *comment ? comment : "")) die_nomem(); /* from */ + + sql_exec(info,&query,4,params); /* log (ignore errors) */ + if (stralloc_0(&addr)) + logaddr(table,event,addr.s,comment); /* also log to old log */ + return 1; /* desired effect */ +} + /* This routine inserts the cookie into table_cookie. We log arrival of * the message (done=0). */ void sub_sql_tagmsg(struct subdbinfo *info, @@ -275,7 +392,7 @@ void sub_sql_tagmsg(struct subdbinfo *info, if (!stralloc_copyb(¶ms[2],strnum,fmt_ulong(strnum,bodysize))) die_nomem(); if (!stralloc_copyb(¶ms[3],strnum,fmt_ulong(strnum,chunk))) die_nomem(); - sql_insert(info,&query,4,params); /* ignore dups */ + sql_exec(info,&query,4,params); /* ignore dups */ if (! (ret = logmsg(msgnum,0L,0L,1))) return; /* log done=1*/ diff --git a/sub_sql.h b/sub_sql.h index 31da662..0ddec93 100644 --- a/sub_sql.h +++ b/sub_sql.h @@ -15,6 +15,11 @@ extern const char sql_logmsg_values_defn[]; extern const char sql_putsubs_where_defn[]; extern const char sql_searchlog_select_defn[]; extern const char sql_searchlog_where_defn[]; +extern const char sql_subscribe_select_where_defn[]; +extern const char sql_subscribe_list_values_defn[]; +extern const char sql_subscribe_delete1_where_defn[]; +extern const char sql_subscribe_delete2_where_defn[]; +extern const char sql_subscribe_slog_values_defn[]; extern const char sql_tagmsg_values_defn[]; extern int sql_table_exists(struct subdbinfo *info, @@ -23,10 +28,10 @@ extern const char *sql_create_table(struct subdbinfo *info, const char *defn); extern const char *sql_drop_table(struct subdbinfo *info, const char *name); -extern int sql_insert(struct subdbinfo *info, - struct stralloc *q, - unsigned int nparams, - struct stralloc *params); +extern int sql_exec(struct subdbinfo *info, + struct stralloc *q, + unsigned int nparams, + struct stralloc *params); extern void *sql_select(struct subdbinfo *info, struct stralloc *q, unsigned int nparams, @@ -63,6 +68,13 @@ extern void sub_sql_searchlog(struct subdbinfo *info, const char *table, char *search, int subwrite()); +extern int sub_sql_subscribe(struct subdbinfo *info, + const char *table, + const char *userhost, + int flagadd, + const char *comment, + const char *event, + int forcehash); extern void sub_sql_tagmsg(struct subdbinfo *info, unsigned long msgnum, const char *hashout,