static SQL_Error SMSDSQL_Query(GSM_SMSDConfig * Config, const char *query, SQL_result * res) { SQL_Error error = SQL_TIMEOUT; int attempts = 1; struct GSM_SMSDdbobj *db = Config->db; for (attempts = 1; attempts <= Config->backend_retries; attempts++) { SMSD_Log(DEBUG_SQL, Config, "Execute SQL: %s", query); error = db->Query(Config, query, res); if (error == SQL_OK) { return error; } if (error != SQL_TIMEOUT){ SMSD_Log(DEBUG_INFO, Config, "SQL failure: %d", error); return error; } SMSD_Log(DEBUG_INFO, Config, "SQL failed (timeout): %s", query); /* We will try to reconnect */ SMSD_Log(DEBUG_INFO, Config, "reconnecting to database!"); while (error != SQL_OK && attempts < Config->backend_retries) { SMSD_Log(DEBUG_INFO, Config, "Reconnecting after %d seconds...", attempts * attempts); sleep(attempts * attempts); db->Free(Config); error = db->Connect(Config); attempts++; } } return error; }
/* [Re]connects to database */ static GSM_Error SMSDPgSQL_Connect(GSM_SMSDConfig * Config) { unsigned char buf[400]; PGresult *Res; unsigned int port = 5432; char *pport; pport = strstr(Config->host, ":"); if (pport) { *pport++ = '\0'; port = atoi(pport); } sprintf(buf, "host = '%s' user = '******' password = '******' dbname = '%s' port = %d", Config->host, Config->user, Config->password, Config->database, port); SMSDPgSQL_Free(Config); Config->conn.pg = PQconnectdb(buf); if (PQstatus(Config->conn.pg) != CONNECTION_OK) { SMSD_Log(DEBUG_ERROR, Config, "Error connecting to database: %s", PQerrorMessage(Config->conn.pg)); PQfinish(Config->conn.pg); return ERR_DB_CONNECT; } Res = PQexec(Config->conn.pg, "SET NAMES UTF8"); PQclear(Res); SMSD_Log(DEBUG_INFO, Config, "Connected to database: %s on %s. Server version: %d Protocol: %d", PQdb(Config->conn.pg), PQhost(Config->conn.pg), PQserverVersion(Config->conn.pg), PQprotocolVersion(Config->conn.pg)); return ERR_NONE; }
static SQL_Error SMSDSQL_Query(GSM_SMSDConfig * Config, const char *query, SQL_result * res) { SQL_Error error = SQL_TIMEOUT; int attempts; struct GSM_SMSDdbobj *db = Config->db; for (attempts = 1; attempts <= Config->backend_retries; attempts++) { SMSD_Log(DEBUG_SQL, Config, "Execute SQL: %s", query); error = db->Query(Config, query, res); if (error == SQL_OK) { return error; } if (error != SQL_TIMEOUT){ SMSD_Log(DEBUG_INFO, Config, "SQL failure: %d", error); return error; } SMSD_Log(DEBUG_INFO, Config, "SQL failed (timeout): %s", query); /* We will try to reconnect */ error = SMSDSQL_Reconnect(Config); if (error != SQL_OK) { break; } } return error; }
static void SMSDPgSQL_LogError(GSM_SMSDConfig * Config, PGresult * Res) { if (Res == NULL) { SMSD_Log(DEBUG_INFO, Config, "Error: %s", PQerrorMessage(Config->conn.pg)); } else { SMSD_Log(DEBUG_INFO, Config, "Error: %s", PQresultErrorMessage(Res)); } }
/* Connects to database */ static GSM_Error SMSDSQL_Init(GSM_SMSDConfig * Config) { SQL_result res; int version; GSM_Error error; struct GSM_SMSDdbobj *db; const char *escape_char; char buffer[100]; #ifdef WIN32 _tzset(); #else tzset(); #endif db = Config->db; if (db->Connect(Config) != SQL_OK) return ERR_UNKNOWN; error = SMSDSQL_CheckTable(Config, "outbox"); if (error != ERR_NONE) return error; error = SMSDSQL_CheckTable(Config, "outbox_multipart"); if (error != ERR_NONE) return error; error = SMSDSQL_CheckTable(Config, "sentitems"); if (error != ERR_NONE) return error; error = SMSDSQL_CheckTable(Config, "inbox"); if (error != ERR_NONE) return error; escape_char = SMSDSQL_EscapeChar(Config); sprintf(buffer, "SELECT %sVersion%s FROM gammu", escape_char, escape_char); if (SMSDSQL_Query(Config, buffer, &res) != SQL_OK) { db->Free(Config); return ERR_UNKNOWN; } if (db->NextRow(Config, &res) != 1) { SMSD_Log(DEBUG_ERROR, Config, "Failed to seek to first row!"); db->FreeResult(Config, &res); db->Free(Config); return ERR_UNKNOWN; } version = db->GetNumber(Config, &res, 0); db->FreeResult(Config, &res); if (SMSD_CheckDBVersion(Config, version) != ERR_NONE) { db->Free(Config); return ERR_UNKNOWN; } SMSD_Log(DEBUG_INFO, Config, "Connected to Database %s: %s on %s", Config->driver, Config->database, Config->host); return ERR_NONE; }
static void SMSDDBI_LogError(GSM_SMSDConfig * Config) { int rc; const char *msg; rc = dbi_conn_error(Config->conn.dbi, &msg); if (rc == -1) { SMSD_Log(DEBUG_ERROR, Config, "Unknown DBI error!"); } else { SMSD_Log(DEBUG_ERROR, Config, "DBI error %d: %s", rc, msg); } }
const char *SMSDODBC_GetString(GSM_SMSDConfig * Config, SQL_result *res, unsigned int field) { SQLLEN sqllen; int size; SQLRETURN ret; char shortbuffer[1]; if (field > SMSD_ODBC_MAX_RETURN_STRINGS) { SMSD_Log(DEBUG_ERROR, Config, "Field %d returning NULL, too many fields!", field); return NULL; } /* Figure out string length */ ret = SQLGetData(res->odbc, field + 1, SQL_C_CHAR, shortbuffer, 0, &sqllen); if (!SQL_SUCCEEDED(ret)) { SMSDODBC_LogError(Config, ret, SQL_HANDLE_STMT, res->odbc, "SQLGetData(string,0) failed"); return NULL; } /* * This hack seems to be needed to avoid type breakage on Win64, don't ask me why. * * Might be actually bug in MinGW compiler, but when using SQLLEN type bellow * anything fails (it does not match to SQL_NULL_DATA and realloc always fails). */ size = sqllen; /* Did not we get NULL? */ if (size == SQL_NULL_DATA) { SMSD_Log(DEBUG_SQL, Config, "Field %d returning NULL", field); return NULL; } /* Allocate string */ Config->conn.odbc.retstr[field] = realloc(Config->conn.odbc.retstr[field], size + 1); if (Config->conn.odbc.retstr[field] == NULL) { SMSD_Log(DEBUG_ERROR, Config, "Field %d returning NULL, failed to allocate %d bytes of memory", field, size + 1); return NULL; } /* Actually grab result from database */ ret = SQLGetData(res->odbc, field + 1, SQL_C_CHAR, Config->conn.odbc.retstr[field], size + 1, &sqllen); if (!SQL_SUCCEEDED(ret)) { SMSDODBC_LogError(Config, ret, SQL_HANDLE_STMT, res->odbc, "SQLGetData(string) failed"); return NULL; } SMSD_Log(DEBUG_SQL, Config, "Field %d returning string \"%s\"", field, Config->conn.odbc.retstr[field]); return Config->conn.odbc.retstr[field]; }
gboolean SMSDDBI_GetBool(GSM_SMSDConfig * Config, SQL_result *res, unsigned int field) { unsigned int type; const char *value; int num; field++; type = dbi_result_get_field_type_idx(res->dbi, field); switch (type) { case DBI_TYPE_INTEGER: case DBI_TYPE_DECIMAL: #ifdef DBI_TYPE_XDECIMAL case DBI_TYPE_XDECIMAL: #endif num = SMSDDBI_GetNumber(Config, res, field); if (num == -1) { return -1; } else if (num == 0) { return FALSE; } else { return TRUE; } case DBI_TYPE_STRING: value = dbi_result_get_string_idx(res->dbi, field); return GSM_StringToBool(value); case DBI_TYPE_ERROR: default: SMSD_Log(DEBUG_ERROR, Config, "Wrong field type for boolean from DBI: %d", type); return -1; } }
time_t SMSDDBI_GetDate(GSM_SMSDConfig * Config, SQL_result *res, unsigned int field) { unsigned int type; const char *date; field++; type = dbi_result_get_field_type_idx(res->dbi, field); switch (type) { case DBI_TYPE_INTEGER: case DBI_TYPE_DECIMAL: #ifdef DBI_TYPE_XDECIMAL case DBI_TYPE_XDECIMAL: #endif return SMSDDBI_GetNumber(Config, res, field); case DBI_TYPE_STRING: date = dbi_result_get_string_idx(res->dbi, field); return SMSDSQL_ParseDate(Config, date); case DBI_TYPE_DATETIME: return dbi_result_get_datetime_idx(res->dbi, field); case DBI_TYPE_ERROR: default: SMSD_Log(DEBUG_ERROR, Config, "Wrong field type for date from DBI: %d", type); return -1; } }
static void SMSDODBC_LogError(GSM_SMSDConfig * Config, SQLRETURN origret, SQLSMALLINT handle_type, SQLHANDLE handle, const char *message) { SQLSMALLINT i = 0; SQLINTEGER native; SQLCHAR state[ 7 ]; SQLCHAR text[256]; SQLSMALLINT len; SQLRETURN ret; SMSD_Log(DEBUG_ERROR, Config, "%s, Code = %d, ODBC diagnostics:", message, (int)origret); do { ret = SQLGetDiagRec(handle_type, handle, ++i, state, &native, text, sizeof(text), &len ); if (SQL_SUCCEEDED(ret)) { SMSD_Log(DEBUG_ERROR, Config, "%s:%ld:%ld:%s\n", state, (long)i, (long)native, text); } } while (ret == SQL_SUCCESS); }
static SQL_Error SMSDSQL_Reconnect(GSM_SMSDConfig * Config) { SQL_Error error = SQL_TIMEOUT; int attempts; struct GSM_SMSDdbobj *db = Config->db; SMSD_Log(DEBUG_INFO, Config, "Reconnecting to the database!"); for (attempts = 1; attempts <= Config->backend_retries; attempts++) { SMSD_Log(DEBUG_INFO, Config, "Reconnecting after %d seconds...", attempts * attempts); sleep(attempts * attempts); db->Free(Config); error = db->Connect(Config); if (error == SQL_OK) { return error; } } return error; }
static GSM_Error SMSDDBI_Query(GSM_SMSDConfig * Config, const char *query, SQL_result * res) { const char *msg; int rc; res->dbi = NULL; SMSD_Log(DEBUG_SQL, Config, "Execute SQL: %s", query); res->dbi = dbi_conn_query(Config->conn.dbi, query); if (res->dbi != NULL) return ERR_NONE; SMSD_Log(DEBUG_INFO, Config, "SQL failed: %s", query); /* Black magic to decide whether we should bail out or attempt to retry */ rc = dbi_conn_error(Config->conn.dbi, &msg); if (rc != -1) { SMSD_Log(DEBUG_INFO, Config, "SQL failure: %s", msg); if (strstr(msg, "syntax") != NULL) { return ERR_SQL; } if (strstr(msg, "violation") != NULL) { return ERR_SQL; } if (strstr(msg, "violates") != NULL) { return ERR_SQL; } if (strstr(msg, "SQL error") != NULL) { return ERR_SQL; } if (strstr(msg, "duplicate") != NULL) { return ERR_SQL; } if (strstr(msg, "unique") != NULL) { return ERR_SQL; } if (strstr(msg, "need to rewrite") != NULL) { return ERR_SQL; } if (strstr(msg, "locked") != NULL) { return ERR_DB_TIMEOUT; } } return ERR_DB_TIMEOUT; }
static GSM_Error SMSDMySQL_Connect(GSM_SMSDConfig * Config) { unsigned int port = 0; int error; char *pport; char *socketname = NULL; pport = strstr(Config->host, ":"); if (pport) { *pport++ = '\0'; /* Is it port or socket? */ if (strchr("0123456798", *pport) != NULL) { port = atoi(pport); } else { socketname = pport; } } if (Config->conn.my == NULL) { Config->conn.my = malloc(sizeof(MYSQL)); mysql_init(Config->conn.my); } if (Config->conn.my == NULL) { SMSD_Log(DEBUG_ERROR, Config, "MySQL allocation failed!"); return ERR_DB_DRIVER; } if (!mysql_real_connect(Config->conn.my, Config->host, Config->user, Config->password, Config->database, port, socketname, 0)) { SMSD_Log(DEBUG_ERROR, Config, "Error connecting to database!"); SMSDMySQL_LogError(Config); error = mysql_errno(Config->conn.my); if (error == 2006 || error == 2003 || error == 2002) { /* cant connect through socket */ return ERR_DB_TIMEOUT; } return ERR_DB_CONNECT; } /* Try using utf8mb4 if MySQL server supports it */ if (mysql_query(Config->conn.my, "SET NAMES utf8mb4;") != 0) { mysql_query(Config->conn.my, "SET NAMES utf8;"); } SMSD_Log(DEBUG_INFO, Config, "Connected to Database: %s on %s", Config->database, Config->host); return ERR_NONE; }
static int SMSDMySQL_LogError(GSM_SMSDConfig * Config) { int mysql_err; mysql_err = mysql_errno(Config->conn.my); SMSD_Log(DEBUG_ERROR, Config, "Error code: %d, Error: %s", mysql_err, mysql_error(Config->conn.my)); return mysql_err; }
const char *SMSDODBC_GetString(GSM_SMSDConfig * Config, SQL_result *res, unsigned int field) { SQLLEN size; SQLRETURN ret; char shortbuffer[1]; if (field > SMSD_ODBC_MAX_RETURN_STRINGS) { SMSD_Log(DEBUG_ERROR, Config, "Field %d returning NULL, too many fields!", field); return NULL; } /* Figure out string length */ ret = SQLGetData(res->odbc, field + 1, SQL_C_CHAR, shortbuffer, 0, &size); if (!SQL_SUCCEEDED(ret)) { SMSDODBC_LogError(Config, ret, SQL_HANDLE_STMT, res->odbc, "SQLGetData(string,0) failed"); return NULL; } /* Did not we get NULL? */ if (size == SQL_NULL_DATA) { SMSD_Log(DEBUG_SQL, Config, "Field %d returning NULL", field); return NULL; } /* Allocate string */ Config->conn.odbc.retstr[field] = realloc(Config->conn.odbc.retstr[field], size + 1); if (Config->conn.odbc.retstr[field] == NULL) { SMSD_Log(DEBUG_ERROR, Config, "Field %d returning NULL, failed to allocate %ld bytes of memory", field, (long)(size + 1)); return NULL; } /* Actually grab result from database */ ret = SQLGetData(res->odbc, field + 1, SQL_C_CHAR, Config->conn.odbc.retstr[field], size + 1, &size); if (!SQL_SUCCEEDED(ret)) { SMSDODBC_LogError(Config, ret, SQL_HANDLE_STMT, res->odbc, "SQLGetData(string) failed"); return NULL; } SMSD_Log(DEBUG_SQL, Config, "Field %d returning string \"%s\"", field, Config->conn.odbc.retstr[field]); return Config->conn.odbc.retstr[field]; }
long long SMSDDBI_GetNumber(GSM_SMSDConfig * Config, SQL_result *res, unsigned int field) { unsigned int type; field++; type = dbi_result_get_field_type_idx(res->dbi, field); switch (type) { case DBI_TYPE_INTEGER: type = dbi_result_get_field_attribs_idx(res->dbi, field); if ((type & DBI_INTEGER_SIZEMASK) == DBI_INTEGER_SIZE1) { return dbi_result_get_int_idx(res->dbi, field); } else if ((type & DBI_INTEGER_SIZEMASK) == DBI_INTEGER_SIZE2) { return dbi_result_get_int_idx(res->dbi, field); } else if ((type & DBI_INTEGER_SIZEMASK) == DBI_INTEGER_SIZE3) { return dbi_result_get_int_idx(res->dbi, field); } else if ((type & DBI_INTEGER_SIZEMASK) == DBI_INTEGER_SIZE4) { return dbi_result_get_int_idx(res->dbi, field); } else if ((type & DBI_INTEGER_SIZEMASK) == DBI_INTEGER_SIZE8) { return dbi_result_get_longlong_idx(res->dbi, field); } SMSD_Log(DEBUG_ERROR, Config, "Wrong integer field subtype from DBI: %d", type); return -1; case DBI_TYPE_DECIMAL: type = dbi_result_get_field_attribs_idx(res->dbi, field); if ((type & DBI_DECIMAL_SIZEMASK) == DBI_DECIMAL_SIZE4) { return dbi_result_get_int_idx(res->dbi, field); } else if ((type & DBI_DECIMAL_SIZEMASK) == DBI_DECIMAL_SIZE8) { return dbi_result_get_longlong_idx(res->dbi, field); } SMSD_Log(DEBUG_ERROR, Config, "Wrong decimal field subtype from DBI: %d", type); return -1; #ifdef DBI_TYPE_XDECIMAL case DBI_TYPE_XDECIMAL: return dbi_result_get_as_longlong_idx(res->dbi, field); #endif default: SMSD_Log(DEBUG_ERROR, Config, "Wrong field type for number (not INTEGER nor DECIMAL) from DBI: %d", type); return -1; } }
/* Disconnects from a database */ static GSM_Error SMSDSQL_Free(GSM_SMSDConfig * Config) { int i; SMSD_Log(DEBUG_SQL, Config, "Disconnecting from SQL database."); Config->db->Free(Config); /* free configuration */ for(i = 0; i < SQL_QUERY_LAST_NO; i++){ free(Config->SMSDSQL_queries[i]); Config->SMSDSQL_queries[i] = NULL; } return ERR_NONE; }
/* Connects to database */ static GSM_Error SMSDODBC_Connect(GSM_SMSDConfig * Config) { SQLRETURN ret; int field; char driver_name[1000]; SQLSMALLINT len; for (field = 0; field < SMSD_ODBC_MAX_RETURN_STRINGS; field++) { Config->conn.odbc.retstr[field] = NULL; } ret = SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &Config->conn.odbc.env); if (!SQL_SUCCEEDED(ret)) { SMSDODBC_LogError(Config, ret, SQL_HANDLE_ENV, Config->conn.odbc.env, "SQLAllocHandle(ENV) failed"); return ERR_DB_DRIVER; } ret = SQLSetEnvAttr (Config->conn.odbc.env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); if (!SQL_SUCCEEDED(ret)) { SMSDODBC_LogError(Config, ret, SQL_HANDLE_ENV, Config->conn.odbc.env, "SQLSetEnvAttr failed"); return ERR_DB_CONFIG; } ret = SQLAllocHandle (SQL_HANDLE_DBC, Config->conn.odbc.env, &Config->conn.odbc.dbc); if (!SQL_SUCCEEDED(ret)) { SMSDODBC_LogError(Config, ret, SQL_HANDLE_ENV, Config->conn.odbc.env, "SQLAllocHandle(DBC) failed"); return ERR_DB_CONFIG; } ret = SQLConnect(Config->conn.odbc.dbc, (SQLCHAR*)Config->host, SQL_NTS, (SQLCHAR*)Config->user, SQL_NTS, (SQLCHAR*)Config->password, SQL_NTS); if (!SQL_SUCCEEDED(ret)) { SMSDODBC_LogError(Config, ret, SQL_HANDLE_DBC, Config->conn.odbc.dbc, "SQLConnect failed"); return ERR_DB_CONNECT; } ret = SQLGetInfo(Config->conn.odbc.dbc, SQL_DRIVER_NAME, driver_name, sizeof(driver_name), &len); if (!SQL_SUCCEEDED(ret)) { SMSDODBC_LogError(Config, ret, SQL_HANDLE_DBC, Config->conn.odbc.dbc, "SQLGetInfo failed"); return ERR_DB_CONNECT; } else{ SMSD_Log(DEBUG_NOTICE, Config, "Connected to driver %s", driver_name); } return ERR_NONE; }
static GSM_Error SMSDSQL_InitAfterConnect(GSM_SMSDConfig * Config) { SQL_result res; struct GSM_SMSDdbobj *db = Config->db; SQL_Var vars[3] = {{SQL_TYPE_STRING, {NULL}}, {SQL_TYPE_STRING, {NULL}}, {SQL_TYPE_NONE, {NULL}}}; if (SMSDSQL_NamedQuery(Config, Config->SMSDSQL_queries[SQL_QUERY_DELETE_PHONE], NULL, NULL, &res) != SQL_OK) { SMSD_Log(DEBUG_INFO, Config, "Error deleting from database (%s)", __FUNCTION__); return ERR_UNKNOWN; } db->FreeResult(Config, &res); SMSD_Log(DEBUG_INFO, Config, "Inserting phone info"); vars[0].v.s = Config->enable_send ? "yes" : "no"; vars[1].v.s = Config->enable_receive ? "yes" : "no"; if (SMSDSQL_NamedQuery(Config, Config->SMSDSQL_queries[SQL_QUERY_INSERT_PHONE], NULL, vars, &res) != SQL_OK) { SMSD_Log(DEBUG_INFO, Config, "Error inserting into database (%s)", __FUNCTION__); return ERR_UNKNOWN; } db->FreeResult(Config, &res); return ERR_NONE; }
/* quote strings */ char * SMSDMySQL_QuoteString(GSM_SMSDConfig * Config, const char *string) { char *buff; int len = strlen(string); buff = malloc(len*2+3); if (buff == NULL) { SMSD_Log(DEBUG_ERROR, Config, "String allocation for escaping failed!"); return NULL; } buff[0] = '\''; buff[1] = '\0'; mysql_real_escape_string(Config->conn.my, buff+1, string, len); strcat(buff, "'"); return buff; }
static GSM_Error SMSDSQL_CheckTable(GSM_SMSDConfig * Config, const char *table) { SQL_result res; char buffer[200]; SQL_Error error; struct GSM_SMSDdbobj *db = Config->db; const char *escape_char; escape_char = SMSDSQL_EscapeChar(Config); sprintf(buffer, "SELECT %s %sID%s FROM %s %s", SMSDSQL_TopClause(Config, "1"), escape_char, escape_char, table, SMSDSQL_LimitClause(Config, "1")); error = SMSDSQL_Query(Config, buffer, &res); if (error != SQL_OK) { SMSD_Log(DEBUG_ERROR, Config, "Table %s not found, disconnecting!", table); db->Free(Config); return ERR_UNKNOWN; } db->FreeResult(Config, &res); return ERR_NONE; }
static GSM_Error SMSDSQL_RefreshSendStatus(GSM_SMSDConfig * Config, char *ID) { SQL_result res; struct GSM_SMSDdbobj *db = Config->db; SQL_Var vars[2] = { {SQL_TYPE_STRING, {ID}}, {SQL_TYPE_NONE, {NULL}}}; if (SMSDSQL_NamedQuery(Config, Config->SMSDSQL_queries[SQL_QUERY_REFRESH_SEND_STATUS], NULL, vars, &res) != SQL_OK) { SMSD_Log(DEBUG_INFO, Config, "Error writing to database (%s)", __FUNCTION__); return ERR_UNKNOWN; } if (db->AffectedRows(Config, &res) == 0) { db->FreeResult(Config, &res); return ERR_UNKNOWN; } db->FreeResult(Config, &res); return ERR_NONE; }
static GSM_Error SMSDSQL_UpdateRetries(GSM_SMSDConfig * Config, char *ID) { SQL_result res; struct GSM_SMSDdbobj *db = Config->db; SQL_Var vars[3] = { {SQL_TYPE_STRING, {ID}}, {SQL_TYPE_INT, {NULL}}, {SQL_TYPE_NONE, {NULL}}}; vars[1].v.i = Config->retries; if (SMSDSQL_NamedQuery(Config, Config->SMSDSQL_queries[SQL_QUERY_UPDATE_RETRIES], NULL, vars, &res) != SQL_OK) { SMSD_Log(DEBUG_INFO, Config, "Error writing to database (%s)", __FUNCTION__); return ERR_UNKNOWN; } if (db->AffectedRows(Config, &res) == 0) { db->FreeResult(Config, &res); return ERR_UNKNOWN; } db->FreeResult(Config, &res); return ERR_NONE; }
/* Find one multi SMS to sending and return it (or return ERR_EMPTY) * There is also set ID for SMS */ static GSM_Error SMSDSQL_FindOutboxSMS(GSM_MultiSMSMessage * sms, GSM_SMSDConfig * Config, char *ID) { SQL_result res; struct GSM_SMSDdbobj *db = Config->db; int i; time_t timestamp; const char *coding; const char *text; size_t text_len; const char *text_decoded; const char *destination; const char *udh; const char *q; size_t udh_len; SQL_Var vars[3]; vars[0].type = SQL_TYPE_INT; vars[0].v.i = 1; vars[1].type = SQL_TYPE_NONE; while (TRUE) { if (SMSDSQL_NamedQuery(Config, Config->SMSDSQL_queries[SQL_QUERY_FIND_OUTBOX_SMS_ID], NULL, vars, &res) != SQL_OK) { SMSD_Log(DEBUG_INFO, Config, "Error reading from database (%s)", __FUNCTION__); return ERR_UNKNOWN; } if (db->NextRow(Config, &res) != 1) { db->FreeResult(Config, &res); return ERR_EMPTY; } sprintf(ID, "%ld", (long)db->GetNumber(Config, &res, 0)); timestamp = db->GetDate(Config, &res, 1); db->FreeResult(Config, &res); if (timestamp == -1) { SMSD_Log(DEBUG_INFO, Config, "Invalid date for InsertIntoDB."); return ERR_UNKNOWN; } SMSDSQL_Time2String(Config, timestamp, Config->DT, sizeof(Config->DT)); if (SMSDSQL_RefreshSendStatus(Config, ID) == ERR_NONE) { break; } } sms->Number = 0; for (i = 0; i < GSM_MAX_MULTI_SMS; i++) { GSM_SetDefaultSMSData(&sms->SMS[i]); sms->SMS[i].SMSC.Number[0] = 0; sms->SMS[i].SMSC.Number[1] = 0; } for (i = 1; i < GSM_MAX_MULTI_SMS + 1; i++) { vars[0].type = SQL_TYPE_STRING; vars[0].v.s = ID; vars[1].type = SQL_TYPE_INT; vars[1].v.i = i; vars[2].type = SQL_TYPE_NONE; if (i == 1) { q = Config->SMSDSQL_queries[SQL_QUERY_FIND_OUTBOX_BODY]; } else { q = Config->SMSDSQL_queries[SQL_QUERY_FIND_OUTBOX_MULTIPART]; } if (SMSDSQL_NamedQuery(Config, q, NULL, vars, &res) != SQL_OK) { SMSD_Log(DEBUG_ERROR, Config, "Error reading from database (%s)", __FUNCTION__); return ERR_UNKNOWN; } if (db->NextRow(Config, &res) != 1) { db->FreeResult(Config, &res); return ERR_NONE; } coding = db->GetString(Config, &res, 1); text = db->GetString(Config, &res, 0); if (text == NULL) { text_len = 0; } else { text_len = strlen(text); } text_decoded = db->GetString(Config, &res, 4); udh = db->GetString(Config, &res, 2); if (udh == NULL) { udh_len = 0; } else { udh_len = strlen(udh); } sms->SMS[sms->Number].Coding = GSM_StringToSMSCoding(coding); if (sms->SMS[sms->Number].Coding == 0) { if (text == NULL || text_len == 0) { SMSD_Log(DEBUG_NOTICE, Config, "Assuming default coding for text message"); sms->SMS[sms->Number].Coding = SMS_Coding_Default_No_Compression; } else { SMSD_Log(DEBUG_NOTICE, Config, "Assuming 8bit coding for binary message"); sms->SMS[sms->Number].Coding = SMS_Coding_8bit; } } if (text == NULL || text_len == 0) { if (text_decoded == NULL) { SMSD_Log(DEBUG_ERROR, Config, "Message without text!"); return ERR_UNKNOWN; } else { SMSD_Log(DEBUG_NOTICE, Config, "Message: %s", text_decoded); DecodeUTF8(sms->SMS[sms->Number].Text, text_decoded, strlen(text_decoded)); } } else { switch (sms->SMS[sms->Number].Coding) { case SMS_Coding_Unicode_No_Compression: case SMS_Coding_Default_No_Compression: DecodeHexUnicode(sms->SMS[sms->Number].Text, text, text_len); break; case SMS_Coding_8bit: DecodeHexBin(sms->SMS[sms->Number].Text, text, text_len); sms->SMS[sms->Number].Length = text_len / 2; break; default: break; } } if (i == 1) { destination = db->GetString(Config, &res, 6); if (destination == NULL) { SMSD_Log(DEBUG_ERROR, Config, "Message without recipient!"); return ERR_UNKNOWN; } DecodeUTF8(sms->SMS[sms->Number].Number, destination, strlen(destination)); } else { CopyUnicodeString(sms->SMS[sms->Number].Number, sms->SMS[0].Number); } sms->SMS[sms->Number].UDH.Type = UDH_NoUDH; if (udh != NULL && udh_len != 0) { sms->SMS[sms->Number].UDH.Type = UDH_UserUDH; sms->SMS[sms->Number].UDH.Length = udh_len / 2; DecodeHexBin(sms->SMS[sms->Number].UDH.Text, udh, udh_len); } sms->SMS[sms->Number].Class = db->GetNumber(Config, &res, 3); sms->SMS[sms->Number].PDU = SMS_Submit; sms->Number++; if (i == 1) { strcpy(Config->CreatorID, db->GetString(Config, &res, 10)); Config->relativevalidity = db->GetNumber(Config, &res, 8); Config->currdeliveryreport = db->GetBool(Config, &res, 9); /* Is this a multipart message? */ if (!db->GetBool(Config, &res, 7)) { db->FreeResult(Config, &res); break; } } db->FreeResult(Config, &res); } return ERR_NONE; }
/* Save SMS from phone (called Inbox sms - it's in phone Inbox) somewhere */ static GSM_Error SMSDSQL_SaveInboxSMS(GSM_MultiSMSMessage * sms, GSM_SMSDConfig * Config, char **Locations) { SQL_result res, res2; SQL_Var vars[3]; struct GSM_SMSDdbobj *db = Config->db; const char *q, *status; char smstext[3 * GSM_MAX_SMS_LENGTH + 1]; char destinationnumber[3 * GSM_MAX_NUMBER_LENGTH + 1]; char smsc_message[3 * GSM_MAX_NUMBER_LENGTH + 1]; int i; time_t t_time1, t_time2; gboolean found; long diff; unsigned long long new_id; size_t locations_size = 0, locations_pos = 0; const char *state, *smsc; *Locations = NULL; for (i = 0; i < sms->Number; i++) { EncodeUTF8(destinationnumber, sms->SMS[i].Number); EncodeUTF8(smsc_message, sms->SMS[i].SMSC.Number); if (sms->SMS[i].PDU == SMS_Status_Report) { EncodeUTF8(smstext, sms->SMS[i].Text); SMSD_Log(DEBUG_INFO, Config, "Delivery report: %s to %s", smstext, destinationnumber); if (SMSDSQL_NamedQuery(Config, Config->SMSDSQL_queries[SQL_QUERY_SAVE_INBOX_SMS_SELECT], &sms->SMS[i], NULL, &res) != SQL_OK) { SMSD_Log(DEBUG_INFO, Config, "Error reading from database (%s)", __FUNCTION__); return ERR_UNKNOWN; } found = FALSE; while (db->NextRow(Config, &res)) { smsc = db->GetString(Config, &res, 4); state = db->GetString(Config, &res, 1); SMSD_Log(DEBUG_NOTICE, Config, "Checking for delivery report, SMSC=%s, state=%s", smsc, state); if (strcmp(smsc, smsc_message) != 0) { if (Config->skipsmscnumber[0] == 0 || strcmp(Config->skipsmscnumber, smsc)) { continue; } } if (strcmp(state, "SendingOK") == 0 || strcmp(state, "DeliveryPending") == 0) { t_time1 = db->GetDate(Config, &res, 2); if (t_time1 < 0) { SMSD_Log(DEBUG_ERROR, Config, "Invalid SendingDateTime -1 for SMS TPMR=%i", sms->SMS[i].MessageReference); return ERR_UNKNOWN; } t_time2 = Fill_Time_T(sms->SMS[i].DateTime); diff = t_time2 - t_time1; if (diff > -Config->deliveryreportdelay && diff < Config->deliveryreportdelay) { found = TRUE; break; } else { SMSD_Log(DEBUG_NOTICE, Config, "Delivery report would match, but time delta is too big (%ld), consider increasing DeliveryReportDelay", diff); } } } if (found) { if (!strcmp(smstext, "Delivered")) { q = Config->SMSDSQL_queries[SQL_QUERY_SAVE_INBOX_SMS_UPDATE_DELIVERED]; } else { q = Config->SMSDSQL_queries[SQL_QUERY_SAVE_INBOX_SMS_UPDATE]; } if (!strcmp(smstext, "Delivered")) { status = "DeliveryOK"; } else if (!strcmp(smstext, "Failed")) { status = "DeliveryFailed"; } else if (!strcmp(smstext, "Pending")) { status = "DeliveryPending"; } else if (!strcmp(smstext, "Unknown")) { status = "DeliveryUnknown"; } else { status = ""; } vars[0].type = SQL_TYPE_STRING; vars[0].v.s = status; /* Status */ vars[1].type = SQL_TYPE_INT; vars[1].v.i = (long)db->GetNumber(Config, &res, 0); /* ID */ vars[2].type = SQL_TYPE_NONE; if (SMSDSQL_NamedQuery(Config, q, &sms->SMS[i], vars, &res2) != SQL_OK) { SMSD_Log(DEBUG_INFO, Config, "Error writing to database (%s)", __FUNCTION__); return ERR_UNKNOWN; } db->FreeResult(Config, &res2); } db->FreeResult(Config, &res); continue; } if (sms->SMS[i].PDU != SMS_Deliver) continue; if (SMSDSQL_NamedQuery(Config, Config->SMSDSQL_queries[SQL_QUERY_SAVE_INBOX_SMS_INSERT], &sms->SMS[i], NULL, &res) != SQL_OK) { SMSD_Log(DEBUG_INFO, Config, "Error writing to database (%s)", __FUNCTION__); return ERR_UNKNOWN; } new_id = db->SeqID(Config, "inbox_ID_seq"); if (new_id == 0) { SMSD_Log(DEBUG_INFO, Config, "Failed to get inserted row ID (%s)", __FUNCTION__); return ERR_UNKNOWN; } SMSD_Log(DEBUG_NOTICE, Config, "Inserted message id %lu", (long)new_id); db->FreeResult(Config, &res); if (new_id != 0) { if (locations_pos + 10 >= locations_size) { locations_size += 40; *Locations = (char *)realloc(*Locations, locations_size); assert(*Locations != NULL); if (locations_pos == 0) { *Locations[0] = 0; } } locations_pos += sprintf((*Locations) + locations_pos, "%lu ", (long)new_id); } if (SMSDSQL_NamedQuery(Config, Config->SMSDSQL_queries[SQL_QUERY_UPDATE_RECEIVED], &sms->SMS[i], NULL, &res2) != SQL_OK) { SMSD_Log(DEBUG_INFO, Config, "Error updating number of received messages (%s)", __FUNCTION__); return ERR_UNKNOWN; } db->FreeResult(Config, &res2); } return ERR_NONE; }
/* Find one multi SMS to sending and return it (or return ERR_EMPTY) * There is also set ID for SMS * File extension convention: * OUTxxxxx.txt : normal text SMS * Options appended to the extension applying to this SMS only: * d: delivery report requested * f: flash SMS * b: WAP bookmark as name,URL * e.g. OUTG20040620_193810_123_+4512345678_xpq.txtdf * is a flash text SMS requesting delivery reports */ static GSM_Error SMSDFiles_FindOutboxSMS(GSM_MultiSMSMessage * sms, GSM_SMSDConfig * Config, char *ID) { GSM_MultiPartSMSInfo SMSInfo; GSM_WAPBookmark Bookmark; char FileName[100], FullName[400]; unsigned char Buffer[(GSM_MAX_SMS_LENGTH * GSM_MAX_MULTI_SMS + 1) * 2]; unsigned char Buffer2[(GSM_MAX_SMS_LENGTH * GSM_MAX_MULTI_SMS + 1) * 2]; FILE *File; int i, len, phlen; char *pos1, *pos2, *options = NULL; gboolean backup = FALSE; #ifdef GSM_ENABLE_BACKUP GSM_SMS_Backup smsbackup; GSM_Error error; #endif #ifdef WIN32 struct _finddata_t c_file; intptr_t hFile; strcpy(FullName, Config->outboxpath); strcat(FullName, "OUT*.txt*"); hFile = _findfirst(FullName, &c_file); if (hFile == -1) { strcpy(FullName, Config->outboxpath); strcat(FullName, "OUT*.smsbackup*"); hFile = _findfirst(FullName, &c_file); backup = TRUE; } if (hFile == -1) { return ERR_EMPTY; } else { strcpy(FileName, c_file.name); } _findclose(hFile); #elif defined(HAVE_DIRBROWSING) struct dirent **namelist = NULL; int cur_file, num_files; char *pos; strcpy(FullName, Config->outboxpath); FullName[strlen(Config->outboxpath) - 1] = '\0'; num_files = scandir(FullName, &namelist, 0, alphasort); for (cur_file = 0; cur_file < num_files; cur_file++) { /* Hidden file or current/parent directory */ if (namelist[cur_file]->d_name[0] == '.') { continue; } /* We care only about files starting with out */ if (strncasecmp(namelist[cur_file]->d_name, "out", 3) != 0) { continue; } /* Check extension */ pos = strrchr(namelist[cur_file]->d_name, '.'); if (pos == NULL) { continue; } if (strncasecmp(pos, ".txt", 4) == 0) { /* We have found text file */ backup = FALSE; break; } if (strncasecmp(pos, ".smsbackup", 10) == 0) { /* We have found a SMS backup file */ backup = TRUE; break; } } /* Remember file name */ if (cur_file < num_files) { strcpy(FileName, namelist[cur_file]->d_name); } /* Free scandir result */ for (i = 0; i < num_files; i++) { free(namelist[i]); } free(namelist); namelist = NULL; /* Did we actually find something? */ if (cur_file >= num_files) { return ERR_EMPTY; } #else return ERR_NOTSUPPORTED; #endif strcpy(FullName, Config->outboxpath); strcat(FullName, FileName); if (backup) { #ifdef GSM_ENABLE_BACKUP /* Remember ID */ strcpy(ID, FileName); /* Load backup */ GSM_ClearSMSBackup(&smsbackup); error = GSM_ReadSMSBackupFile(FullName, &smsbackup); if (error != ERR_NONE) { return error; } /* Copy it to our message */ sms->Number = 0; for (i = 0; smsbackup.SMS[i] != NULL; i++) { sms->SMS[sms->Number++] = *smsbackup.SMS[i]; } /* Free memory */ GSM_FreeSMSBackup(&smsbackup); /* Set delivery report flag */ if (sms->SMS[0].PDU == SMS_Status_Report) { Config->currdeliveryreport = 1; } else { Config->currdeliveryreport = -1; } #else SMSD_Log(DEBUG_ERROR, Config, "SMS backup loading disabled at compile time!"); return ERR_DISABLED; #endif } else { options = strrchr(FileName, '.') + 4; File = fopen(FullName, "rb"); if (File == NULL) { return ERR_CANTOPENFILE; } len = fread(Buffer, 1, sizeof(Buffer) - 2, File); fclose(File); if ((len < 2) || (len >= 2 && ((Buffer[0] != 0xFF || Buffer[1] != 0xFE) && (Buffer[0] != 0xFE || Buffer[1] != 0xFF)))) { if (len > GSM_MAX_SMS_LENGTH * GSM_MAX_MULTI_SMS) len = GSM_MAX_SMS_LENGTH * GSM_MAX_MULTI_SMS; EncodeUnicode(Buffer2, Buffer, len); len = len * 2; memmove(Buffer, Buffer2, len); Buffer[len] = 0; Buffer[len + 1] = 0; } else { Buffer[len] = 0; Buffer[len + 1] = 0; /* Possibly convert byte order */ ReadUnicodeFile(Buffer2, Buffer); } GSM_ClearMultiPartSMSInfo(&SMSInfo); sms->Number = 0; SMSInfo.ReplaceMessage = 0; SMSInfo.Entries[0].Buffer = Buffer2; SMSInfo.Class = -1; SMSInfo.EntriesNum = 1; Config->currdeliveryreport = -1; if (strchr(options, 'd')) Config->currdeliveryreport = 1; if (strchr(options, 'f')) SMSInfo.Class = 0; /* flash SMS */ if (strcasecmp(Config->transmitformat, "unicode") == 0) { SMSInfo.Entries[0].ID = SMS_ConcatenatedTextLong; SMSInfo.UnicodeCoding = TRUE; } else if (strcasecmp(Config->transmitformat, "7bit") == 0) { SMSInfo.Entries[0].ID = SMS_ConcatenatedTextLong; SMSInfo.UnicodeCoding = FALSE; } else { /* auto */ SMSInfo.Entries[0].ID = SMS_ConcatenatedAutoTextLong; } if (strchr(options, 'b')) { // WAP bookmark as title,URL SMSInfo.Entries[0].Buffer = NULL; SMSInfo.Entries[0].Bookmark = &Bookmark; SMSInfo.Entries[0].ID = SMS_NokiaWAPBookmarkLong; SMSInfo.Entries[0].Bookmark->Location = 0; pos2 = mywstrstr(Buffer2, "\0,"); if (pos2 == NULL) { pos2 = Buffer2; } else { *pos2 = '\0'; pos2++; *pos2 = '\0'; pos2++; // replace comma by zero } len = UnicodeLength(Buffer2); if (len > 50) { len = 50; } memmove(&SMSInfo.Entries[0].Bookmark->Title, Buffer2, len * 2); pos1 = &SMSInfo.Entries[0].Bookmark->Title[0] + len * 2; *pos1 = '\0'; pos1++; *pos1 = '\0'; len = UnicodeLength(pos2); if (len > 255) { len = 255; } memmove(&SMSInfo.Entries[0].Bookmark->Address, pos2, len * 2); pos1 = &SMSInfo.Entries[0].Bookmark->Address[0] + len * 2; *pos1 = '\0'; pos1++; *pos1 = '\0'; } GSM_EncodeMultiPartSMS(GSM_GetDebug(Config->gsm), &SMSInfo, sms); strcpy(ID, FileName); pos1 = FileName; for (i = 1; i <= 3 && pos1 != NULL; i++) { pos1 = strchr(++pos1, '_'); } if (pos1 != NULL) { /* OUT<priority><date>_<time>_<serialno>_<phone number>_<anything>.txt */ pos2 = strchr(++pos1, '_'); if (pos2 != NULL) { phlen = strlen(pos1) - strlen(pos2); } else { /* something wrong */ return ERR_UNKNOWN; } } else if (i == 2) { /* OUTxxxxxxx.txt or OUTxxxxxxx */ pos1 = &FileName[3]; pos2 = strchr(pos1, '.'); if (pos2 == NULL) { phlen = strlen(pos1); } else { phlen = strlen(pos1) - strlen(pos2); } } else if (i == 4) { /* OUT<priority>_<phone number>_<serialno>.txt */ pos1 = strchr(FileName, '_'); pos2 = strchr(++pos1, '_'); phlen = strlen(pos1) - strlen(pos2); } else { /* something wrong */ return ERR_UNKNOWN; } for (len = 0; len < sms->Number; len++) { EncodeUnicode(sms->SMS[len].Number, pos1, phlen); } } if (sms->Number != 0) { DecodeUnicode(sms->SMS[0].Number, Buffer); if (options != NULL && strchr(options, 'b')) { // WAP bookmark as title,URL SMSD_Log(DEBUG_NOTICE, Config, "Found %i sms to \"%s\" with bookmark \"%s\" cod %i lgt %i udh: t %i l %i dlr: %i fls: %i", sms->Number, Buffer, DecodeUnicodeString(SMSInfo.Entries[0].Bookmark->Address), sms->SMS[0].Coding, sms->SMS[0].Length, sms->SMS[0].UDH.Type, sms->SMS[0].UDH.Length, Config->currdeliveryreport, SMSInfo.Class); } else { SMSD_Log(DEBUG_NOTICE, Config, "Found %i sms to \"%s\" with text \"%s\" cod %i lgt %i udh: t %i l %i dlr: %i fls: %i", sms->Number, Buffer, DecodeUnicodeString(sms->SMS[0].Text), sms->SMS[0].Coding, sms->SMS[0].Length, sms->SMS[0].UDH.Type, sms->SMS[0].UDH.Length, Config->currdeliveryreport, sms->SMS[0].Class); } } else { SMSD_Log(DEBUG_NOTICE, Config, "error: SMS-count = 0"); } return ERR_NONE; }
/* Save SMS from phone (called Inbox sms - it's in phone Inbox) somewhere */ static GSM_Error SMSDFiles_SaveInboxSMS(GSM_MultiSMSMessage * sms, GSM_SMSDConfig * Config, char **Locations) { GSM_Error error = ERR_NONE; int i, j; unsigned char FileName[100], FullName[400], ext[4], buffer[64], buffer2[400]; gboolean done; FILE *file; size_t locations_size = 0, locations_pos = 0; #ifdef GSM_ENABLE_BACKUP GSM_SMS_Backup backup; #endif *Locations = NULL; j = 0; done = FALSE; for (i = 0; i < sms->Number && !done; i++) { strcpy(ext, "txt"); if (sms->SMS[i].Coding == SMS_Coding_8bit) strcpy(ext, "bin"); DecodeUnicode(sms->SMS[i].Number, buffer2); /* we loop on yy for the first SMS assuming that if xxxx_yy_00.ext is absent, any xxxx_yy_01,02, must be garbage, that can be overwritten */ file = NULL; do { sprintf(FileName, "IN%02d%02d%02d_%02d%02d%02d_%02i_%s_%02i.%s", sms->SMS[i].DateTime.Year, sms->SMS[i].DateTime.Month, sms->SMS[i].DateTime.Day, sms->SMS[i].DateTime.Hour, sms->SMS[i].DateTime.Minute, sms->SMS[i].DateTime.Second, j, buffer2, i, ext); strcpy(FullName, Config->inboxpath); strcat(FullName, FileName); if (file) fclose(file); file = fopen(FullName, "r"); } while ((i == 0) && file != NULL && (++j < 100)); if (file) { fclose(file); if (i == 0) { SMSD_Log(DEBUG_ERROR, Config, "Cannot save %s. No available file names", FileName); return ERR_CANTOPENFILE; } } errno = 0; if ((sms->SMS[i].PDU == SMS_Status_Report) && strcasecmp(Config->deliveryreport, "log") == 0) { strcpy(buffer, DecodeUnicodeString(sms->SMS[i].Number)); SMSD_Log(DEBUG_NOTICE, Config, "Delivery report: %s to %s, message reference 0x%02x", DecodeUnicodeString(sms->SMS[i].Text), buffer, sms->SMS[i].MessageReference); } else { if (locations_pos + strlen(FileName) + 2 >= locations_size) { locations_size += strlen(FileName) + 30; *Locations = (char *)realloc(*Locations, locations_size); assert(*Locations != NULL); if (locations_pos == 0) { *Locations[0] = 0; } } strcat(*Locations, FileName); strcat(*Locations, " "); locations_pos += strlen(FileName) + 1; if (strcasecmp(Config->inboxformat, "detail") == 0) { #ifndef GSM_ENABLE_BACKUP SMSD_Log(DEBUG_ERROR, Config, "Saving in detail format not compiled in!"); #else for (j = 0; j < sms->Number; j++) { backup.SMS[j] = &sms->SMS[j]; } backup.SMS[sms->Number] = NULL; error = GSM_AddSMSBackupFile(FullName, &backup); done = TRUE; #endif } else { file = fopen(FullName, "wb"); if (file == NULL) { SMSD_LogErrno(Config, "Cannot save file!"); return ERR_CANTOPENFILE; } switch (sms->SMS[i].Coding) { case SMS_Coding_Unicode_No_Compression: case SMS_Coding_Default_No_Compression: DecodeUnicode(sms->SMS[i].Text, buffer2); if (strcasecmp(Config->inboxformat, "unicode") == 0) { buffer[0] = 0xFE; buffer[1] = 0xFF; chk_fwrite(buffer, 1, 2, file); chk_fwrite(sms->SMS[i].Text, 1, strlen(buffer2) * 2, file); } else { chk_fwrite(buffer2, 1, strlen(buffer2), file); } break; case SMS_Coding_8bit: chk_fwrite(sms->SMS[i].Text, 1, (size_t) sms->SMS[i].Length, file); default: break; } fclose(file); } if (error != ERR_NONE) { return error; } SMSD_Log(DEBUG_INFO, Config, "%s %s", (sms->SMS[i].PDU == SMS_Status_Report ? "Delivery report" : "Received"), FileName); } } return ERR_NONE; fail: return ERR_WRITING_FILE; }
static SQL_Error SMSDSQL_NamedQuery(GSM_SMSDConfig * Config, const char *sql_query, GSM_SMSMessage *sms, const SQL_Var *params, SQL_result * res) { char buff[65536], *ptr, c, static_buff[8192]; char *buffer2, *end; const char *to_print, *q = sql_query; int int_to_print; int numeric; int n, argc = 0; struct GSM_SMSDdbobj *db = Config->db; if (params != NULL) { while (params[argc].type != SQL_TYPE_NONE) argc++; } ptr = buff; do { if (*q != '%') { *ptr++ = *q; continue; } c = *(++q); if( c >= '0' && c <= '9'){ n = strtoul(q, &end, 10) - 1; if (n < argc && n >= 0) { switch(params[n].type){ case SQL_TYPE_INT: ptr += sprintf(ptr, "%i", params[n].v.i); break; case SQL_TYPE_STRING: buffer2 = db->QuoteString(Config, params[n].v.s); memcpy(ptr, buffer2, strlen(buffer2)); ptr += strlen(buffer2); free(buffer2); break; default: SMSD_Log(DEBUG_ERROR, Config, "SQL: unknown type: %i (application bug) in query: `%s`", params[n].type, sql_query); return SQL_BUG; break; } } else { SMSD_Log(DEBUG_ERROR, Config, "SQL: wrong number of parameter: %i (max %i) in query: `%s`", n+1, argc, sql_query); return SQL_BUG; } q = end - 1; continue; } numeric = 0; to_print = NULL; switch (c) { case 'I': to_print = Config->Status->IMEI; break; case 'P': to_print = Config->PhoneID; break; case 'N': snprintf(static_buff, sizeof(static_buff), "Gammu %s, %s, %s", GAMMU_VERSION, GetOS(), GetCompiler()); to_print = static_buff; break; case 'A': to_print = Config->CreatorID; break; default: if (sms != NULL) { switch (c) { case 'R': EncodeUTF8(static_buff, sms->Number); to_print = static_buff; break; case 'F': EncodeUTF8(static_buff, sms->SMSC.Number); to_print = static_buff; break; case 'u': if (sms->UDH.Type != UDH_NoUDH) { EncodeHexBin(static_buff, sms->UDH.Text, sms->UDH.Length); to_print = static_buff; }else{ to_print = ""; } break; case 'x': int_to_print = sms->Class; numeric = 1; break; case 'c': to_print = GSM_SMSCodingToString(sms->Coding); break; case 't': int_to_print = sms->MessageReference; numeric = 1; break; case 'E': switch (sms->Coding) { case SMS_Coding_Unicode_No_Compression: case SMS_Coding_Default_No_Compression: EncodeHexUnicode(static_buff, sms->Text, UnicodeLength(sms->Text)); break; case SMS_Coding_8bit: EncodeHexBin(static_buff, sms->Text, sms->Length); break; default: *static_buff = '\0'; break; } to_print = static_buff; break; case 'T': switch (sms->Coding) { case SMS_Coding_Unicode_No_Compression: case SMS_Coding_Default_No_Compression: EncodeUTF8(static_buff, sms->Text); to_print = static_buff; break; default: to_print = ""; break; } break; case 'V': if (sms->SMSC.Validity.Format == SMS_Validity_RelativeFormat) { int_to_print = sms->SMSC.Validity.Relative; } else { int_to_print = -1; } numeric = 1; break; case 'C': SMSDSQL_Time2String(Config, Fill_Time_T(sms->SMSCTime), static_buff, sizeof(static_buff)); to_print = static_buff; break; case 'd': SMSDSQL_Time2String(Config, Fill_Time_T(sms->DateTime), static_buff, sizeof(static_buff)); to_print = static_buff; break; case 'e': int_to_print = sms->DeliveryStatus; numeric = 1; break; default: SMSD_Log(DEBUG_ERROR, Config, "SQL: uexpected char '%c' in query: %s", c, sql_query); return SQL_BUG; } /* end of switch */ } else { SMSD_Log(DEBUG_ERROR, Config, "Syntax error in query.. uexpected char '%c' in query: %s", c, sql_query); return SQL_BUG; } break; } /* end of switch */ if (numeric) { ptr += sprintf(ptr, "%i", int_to_print); } else if (to_print != NULL) { buffer2 = db->QuoteString(Config, to_print); memcpy(ptr, buffer2, strlen(buffer2)); ptr += strlen(buffer2); free(buffer2); } else { memcpy(ptr, "NULL", 4); ptr += 4; } } while (*(++q) != '\0'); *ptr = '\0'; return SMSDSQL_Query(Config, buff, res); }
/* Connects to database */ static GSM_Error SMSDDBI_Connect(GSM_SMSDConfig * Config) { int rc; struct GSM_SMSDdbobj *db = Config->db; rc = dbi_initialize(Config->driverspath); if (rc == 0) { SMSD_Log(DEBUG_ERROR, Config, "DBI did not find any drivers, try using DriversPath option"); dbi_shutdown(); return ERR_DB_DRIVER; } else if (rc < 0) { SMSD_Log(DEBUG_ERROR, Config, "DBI failed to initialize!"); return ERR_DB_DRIVER; } Config->conn.dbi = dbi_conn_new(Config->driver); if (Config->conn.dbi == NULL) { SMSD_Log(DEBUG_ERROR, Config, "DBI failed to init %s driver!", Config->driver); dbi_shutdown(); return ERR_DB_DRIVER; } else { SMSD_Log(DEBUG_SQL, Config, "Using DBI driver '%s'", dbi_driver_get_name(dbi_conn_get_driver(Config->conn.dbi))); } dbi_conn_error_handler(Config->conn.dbi, SMSDDBI_Callback, Config); if (dbi_conn_set_option(Config->conn.dbi, "sqlite_dbdir", Config->dbdir) != 0) { SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set sqlite_dbdir!"); SMSDDBI_Free(Config); return ERR_DB_CONFIG; } if (dbi_conn_set_option(Config->conn.dbi, "sqlite3_dbdir", Config->dbdir) != 0) { SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set sqlite3_dbdir!"); SMSDDBI_Free(Config); return ERR_DB_CONFIG; } if (dbi_conn_set_option(Config->conn.dbi, "host", Config->host) != 0) { SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set host!"); SMSDDBI_Free(Config); return ERR_DB_CONFIG; } if (dbi_conn_set_option(Config->conn.dbi, "username", Config->user) != 0) { SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set username!"); SMSDDBI_Free(Config); return ERR_DB_CONFIG; } if (dbi_conn_set_option(Config->conn.dbi, "password", Config->password) != 0) { SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set password!"); SMSDDBI_Free(Config); return ERR_DB_CONFIG; } if (dbi_conn_set_option(Config->conn.dbi, "dbname", Config->database) != 0) { SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set dbname!"); SMSDDBI_Free(Config); return ERR_DB_CONFIG; } if (dbi_conn_set_option(Config->conn.dbi, "encoding", "UTF-8") != 0) { SMSD_Log(DEBUG_ERROR, Config, "DBI failed to set encoding!"); SMSDDBI_Free(Config); return ERR_DB_CONFIG; } if (dbi_conn_connect(Config->conn.dbi) != 0) { SMSD_Log(DEBUG_ERROR, Config, "DBI failed to connect!"); SMSDDBI_Free(Config); return ERR_DB_CONNECT; } Config->db = db; return ERR_NONE; }