/* ** pgsql_dispatch.c ** $Id: pgsql_dispatch.c,v 1.7 2002/07/10 16:11:15 brian Exp $ */ #include "mod_mp3.h" #include #include "pgsql_dispatch.h" void pgsql_db_connect(pool *p, pgsql_context *context) { char *conn_info = NULL; if (! context->dbh) { if (context->hostname) conn_info = ap_psprintf(p, "host='%s'", context->hostname); else conn_info = ap_psprintf(p, " "); /* make sure we have something */ if (context->database) conn_info = ap_psprintf(p, "%s dbname='%s'", conn_info, context->database); if (context->user) conn_info = ap_psprintf(p, "%s user='%s'", conn_info, context->user); if (context->password) conn_info = ap_psprintf(p, "%s password='%s'", conn_info, context->password); #ifdef DEBUG printf("conn_info: %s\n", conn_info); #endif context->dbh = PQconnectdb(conn_info); if (PQstatus(context->dbh) != CONNECTION_OK) { /*** FIX ME ***/ fprintf(stderr, "Failed connect to postgresql: %s", PQerrorMessage(context->dbh)); exit(1); } } } /* This will not scale, it will eat up too much memory after playing for too long */ void pgsql_row2bank(pool *p, PGresult *res, int row, mp3_data *bank) { bank->name = ap_pstrdup(p, PQgetvalue(res, row, 0)); bank->filename = ap_pstrdup(p, PQgetvalue(res, row, 1)); bank->signature = ap_pstrdup(p, PQgetvalue(res, row, 2)); bank->artist = ap_pstrdup(p, PQgetvalue(res, row, 3)); bank->album = ap_pstrdup(p, PQgetvalue(res, row, 4)); bank->comment = ap_pstrdup(p, PQgetvalue(res, row, 5)); bank->track = ap_pstrdup(p, PQgetvalue(res, row, 6)); bank->year = ap_pstrdup(p, PQgetvalue(res, row, 7)); bank->genre = ap_pstrdup(p, PQgetvalue(res, row, 8)); } MP3_EXPORT(void *) pgsql_create(pool *p) { pgsql_context *context = NULL; context = ap_pcalloc(p, sizeof(pgsql_context)); context->dbh = NULL; context->sth = NULL; context->row = 0; context->hostname = NULL; context->user = NULL; context->password = NULL; context->database = NULL; context->table = NULL; return context; } MP3_EXPORT(void) pgsql_cleanup(void *data) { pgsql_context *context = (pgsql_context *)data; if (context->sth) { PQclear(context->sth); context->sth = NULL; } /* should we also disconnect here? */ } MP3_EXPORT(int) pgsql_request(void *info, void *cfg, pool *p) { pgsql_context *context = (pgsql_context *)info; pgsql_db_connect(p, context); context->sth = NULL; ap_register_cleanup(p, context, pgsql_cleanup, ap_null_cleanup); return 0; } const char * pgsql_quote(pool *p, const char *string, const int len) { /* my home-brew quoter, hope it works */ int i, j, count; char *new_str; count = 0; for (i = 0; i < len; i++) { /* first count how many "'" we find */ if (string[i] == '\'' || string[i] == '\\') count++; } new_str = ap_pcalloc(p, sizeof(char)*(len+(2*count)+1)); memset(new_str, 0, sizeof(char)*(len+(2*count)+1)); j = 0; for (i=0; i < len; i++) { switch (string[i]) { case '\'': /* an apostrophe */ new_str[j++] = '\\'; new_str[j++] = '\''; break; case '\\': /* a backslash */ new_str[j++] = '\\'; new_str[j++] = '\\'; break; default: new_str[j++] = string[i]; break; } } new_str[j] = '\0'; return new_str; } MP3_EXPORT(mp3_data *) pgsql_get(void *info, pool *p, char *signature) { pgsql_context *context = (pgsql_context *)info; char query[HUGE_STRING_LEN]; memset(query, 0, sizeof(char) * HUGE_STRING_LEN); snprintf(query, HUGE_STRING_LEN, PG_MP3_GET, context->table, signature); if (context->sth) { PQclear(context->sth); } context->sth = PQexec(context->dbh, query); if (! context || PQresultStatus(context->sth) != PGRES_TUPLES_OK) { /*** FIX ME ***/ fprintf(stderr, "Failed to select row: %s", PQresultErrorMessage(context->sth)); return NULL; } if (PQntuples(context->sth) < 1) { PQclear(context->sth); context->sth = NULL; return NULL; } pgsql_row2bank(p, context->sth, 0, &context->bank); return &context->bank; } MP3_EXPORT(int) pgsql_set(void *info, pool *p, mp3_data *bank) { pgsql_context *context = (pgsql_context *)info; char query[HUGE_STRING_LEN]; const char *name, *filename, *signature, *artist; const char *album, *comment, *track, *year, *genre; PGresult *res; /* temporary, for command execution */ pgsql_db_connect(p, context); memset(query, 0, sizeof(char) * HUGE_STRING_LEN); name = filename = signature = artist = NULL; album = comment = track = year = genre = NULL; if (bank->name) name = pgsql_quote(p, bank->name, strlen(bank->name)); if (bank->filename) filename = pgsql_quote(p, bank->filename, strlen(bank->filename)); if (bank->signature) signature = pgsql_quote(p, bank->signature, strlen(bank->signature)); if (bank->artist) artist = pgsql_quote(p, bank->artist, strlen(bank->artist)); if (bank->album) album = pgsql_quote(p, bank->album, strlen(bank->album)); if (bank->comment) comment = pgsql_quote(p, bank->comment, strlen(bank->comment)); if (bank->track) track = pgsql_quote(p, bank->track, strlen(bank->track)); if (bank->year) year = pgsql_quote(p, bank->year, strlen(bank->year)); if (bank->genre) genre = pgsql_quote(p, bank->genre, strlen(bank->genre)); snprintf(query, HUGE_STRING_LEN, PG_MP3_INSERT, context->table , name, filename, signature, artist, album, comment, track, year, genre); res = PQexec(context->dbh, query); if (! res || (PQresultStatus(res) != PGRES_COMMAND_OK && ! strstr(PQresultErrorMessage(res), "duplicate"))) { /* if the error message says we can't insert a duplicate key, nevermind */ /*** FIX ME ***/ fprintf(stderr, "Failed to insert row: %s", PQresultErrorMessage(res)); } PQclear(res); return 0; } MP3_EXPORT(mp3_data *) pgsql_each(void *info, pool *p, array_header *files, const char *token, int random) { pgsql_context *context = (pgsql_context *)info; char query[HUGE_STRING_LEN]; const char *list = NULL; const char *pquery = NULL; char **find_files = NULL; int x = 0; pgsql_db_connect(p, context); if (PQresultStatus(context->sth) != PGRES_TUPLES_OK) { PQclear(context->sth); context->sth = NULL; if (files) { /* We should create a temp pool while doing this -Brian */ find_files = (char **)files->elts; list = ap_psprintf(p, ""); for(x = 0; x < files->nelts; x++) { list = ap_psprintf(p, "%s '%s'", list, find_files[x]); if (x < (files->nelts - 1)) list = ap_psprintf(p, "%s '%s'", list, find_files[x]); else list = ap_psprintf(p, "%s, '%s'", list); } /* Final one we don't need a comma for */ if (random) { context->each_sql = ap_psprintf(p, PG_MP3_GET_LIST_RANDOM, context->table, list); } else { context->each_sql = ap_psprintf(p, PG_MP3_GET_LIST, context->table, list); } pquery = context->each_sql; } else if (token) { /* token is not escaped it should be */ if (random) { snprintf(query, HUGE_STRING_LEN, PG_MP3_GETS_TOKEN_RANDOM, context->table, context->table_token, token); } else { snprintf(query, HUGE_STRING_LEN, PG_MP3_GETS_TOKEN, context->table, context->table_token, token); } pquery = query; } else { if (random) { snprintf(query, HUGE_STRING_LEN, PG_MP3_GETS_RANDOM, context->table); } else { snprintf(query, HUGE_STRING_LEN, PG_MP3_GETS, context->table); } pquery = query; } context->sth = PQexec(context->dbh, pquery); if (! context->sth || PQresultStatus(context->sth) != PGRES_TUPLES_OK) { fprintf(stderr, "Failed to select row: %s", PQresultErrorMessage(context->sth)); return NULL; } /* Look at the MySQL dispatch, why not store the row?? */ context->row = 0; } /* we have the data in context->sth */ if (context->row >= PQntuples(context->sth)) { PQclear(context->sth); context->sth = NULL; return NULL; } pgsql_row2bank(p, context->sth, context->row++, &context->bank); return &context->bank; } /* This is not finished */ MP3_EXPORT(array_header *) pgsql_search(void *info, pool *p, const char *pattern, int limit) { pgsql_context *context = (pgsql_context *)info; array_header *signatures = NULL; char query[HUGE_STRING_LEN]; int count = 0; memset(query, 0, sizeof(char) * HUGE_STRING_LEN); if(pattern) { /* I will improve this later */ snprintf(query, HUGE_STRING_LEN, PG_MP3_FETCH_PATTERN, context->table, pattern); } else { snprintf(query, HUGE_STRING_LEN, PG_MP3_FETCH, context->table); } return NULL; } MP3_EXPORT(const char *) pgsql_add_connect_info(cmd_parms *cmd, void *mconfig, char *hostname, char *user, char *password) { mp3_conf *cfg = (mp3_conf *)mconfig; pgsql_context *context = (pgsql_context *)cfg->context; if (hostname && strcasecmp("null", hostname)) { context->hostname = ap_pstrdup(cmd->pool, hostname); } if(user && strcasecmp("null", user)) { context->user = ap_pstrdup(cmd->pool, user); } if(password && strcasecmp("null", password)) { context->password = ap_pstrdup(cmd->pool, password); } return NULL; } MP3_EXPORT(const char *) pgsql_add_database_info(cmd_parms *cmd, void *mconfig, char *database, char *table) { mp3_conf *cfg = (mp3_conf *) mconfig; pgsql_context *context = (pgsql_context *)cfg->context; context->database = ap_pstrdup(cmd->pool, database); context->table = ap_pstrdup(cmd->pool, table); return NULL; } MP3_EXPORT(const char *) pgsql_add_token_table(cmd_parms *cmd, void *mconfig, char *table) { mp3_conf *cfg = (mp3_conf *) mconfig; pgsql_context *context = (pgsql_context *)cfg->context; context->table_token = ap_pstrdup(cmd->pool, table); return NULL; } MP3_EXPORT(int) pgsql_count(void *info, pool *p, array_header *files, const char *token) { pgsql_context *context = (pgsql_context *)info; char query[HUGE_STRING_LEN]; const char *list = ""; const char *pquery = NULL; char **find_files = NULL; int x = 0; int count = 0; pgsql_db_connect(p, context); memset(query, 0, sizeof(char) * HUGE_STRING_LEN); if(files) { if(!context->each_sql) { /* We should create a temp pool while doing this -Brian */ find_files = (char **)files->elts; for(x = 0; x < files->nelts; x++) { list = ap_psprintf(p, "%s '%s'", list, find_files[x]); if (x < (files->nelts - 1)) list = ap_psprintf(p, "%s '%s'", list, find_files[x]); else list = ap_psprintf(p, "%s '%s',", list); } /* Final one we don't need a comma for */ context->each_sql = ap_psprintf(p, PG_MP3_GET_LIST_COUNT, context->table, list); } pquery = context->each_sql; } else if (token) { /* token is not escaped it should be */ snprintf(query, HUGE_STRING_LEN, PG_MP3_GETS_TOKEN_COUNT, context->table, context->table_token, token); pquery = query; } else { snprintf(query, HUGE_STRING_LEN, PG_MP3_GETS_COUNT, context->table); pquery = query; } /* What is below need to be modified for Pg */ /* if (mysql_real_query(context->dbh, pquery, strlen(pquery))) { fprintf(stderr, "Failed to select row, Error: %s\n", mysql_error(context->dbh)); return 0; } else { context->sth = mysql_store_result(context->dbh); } context->row = mysql_fetch_row(context->sth); count = atoi(context->row[0]); mysql_free_result(context->sth); context->sth = NULL; return count; */ return 0; } mp3_dispatch pgsql = { "pgsql", /* Name of the dispatch */ pgsql_create, /* Create a context */ NULL, /* Init to run when server forks for child */ pgsql_request, /* Init to run when request is made */ pgsql_get, /* Get request */ pgsql_set, /* Set an item request */ pgsql_each, /* Each operator to run through entire list */ pgsql_count, /* Method to count available files */ pgsql_search, /* This returns filenames based on a pattern, if no pattern is give then it should return all signatures in the system.*/ };