Kamis, Januari 19, 2006

ODBC Module for gnokii-smsd

I tried to do some modification on gnokii smsd database module.
Firstly it began when I tried to build a sms handler for incoming message.
Then i want to do it in Linux / other unix based server. but then later I was told that the server will be used is MS Windows.
because i've little experience in doing programming using M$ language, then i think i must find some way to make the sms handler work in Windows environment.
After searching for many references about gnokii for windows, it seems that the smsd part cant be compiled under windows.
Actually I able to compile it under cygwin, but still... dunno why i cant use it.
Then i tried to look at the smsd source code. it seems that the program is designed to work mainly for unix based computer.
Then i looked at the database module part of gnokii smsd. It came to my mind that maybe it will be very helpful if i can write a database module which can be used for many database types. Because currently gnokii smsd support mysql and postgresql, then i think maybe ODBC will be a good choice.
Who knows that someone want to save their smses on other machine, maybe even in windows machine and using database type which hasn't been supported by smsd module (MS SQL Server maybe..)
Actually this code is just a modification from existing modules , i.e mysql and postgresql and I also looked at ODBC specification and sample code.
Because this is my early attempt to code in C, I believe that there will be many thing ugly.
I even actually cant code in C, but just did it with modification of available sample code :D
I've tested the code, it seems to work.
I can compile it under cygwin on windows and try to connect to ODBC (mysql and postgresql server on local machine) and it can connect to DB and (maybe) did the query.
Can someone capable on C coding comment on the code below.. maybe there is something wrong / harmful? Just in case you dont know what gnokii is, just check their homepage at here

/*
S M S D

A Linux/Unix tool for the mobile phones.

This file is part of gnokii.

Gnokii is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

Gnokii is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with gnokii; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Copyright (C) 1999 Pavel Janík ml., Hugh Blemings
Copyright (C) 1999-2005 Jan Derfinak

This file is a module to smsd for db connection through ODBC Protocol.

*/

/* Needed for MS quasi-types */
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include <string.h>
#include <glib.h>
#include "smsd.h"
#include "gnokii.h"
#include "compat.h"

static SQLHDBC connIn;
static SQLHDBC connOut;
static SQLHENV inEnv;
static SQLHENV outEnv;

void DB_Bye (void)
{
SQLDisconnect(connIn);
SQLDisconnect(connOut);
SQLFreeHandle(SQL_HANDLE_DBC, connIn);
SQLFreeHandle(SQL_HANDLE_DBC, connOut);
SQLFreeHandle(SQL_HANDLE_ENV, inEnv);
SQLFreeHandle(SQL_HANDLE_ENV, outEnv);
}


gint DB_ConnectInbox (DBConfig connect)
{
gchar *uid = (connect.user[0] != '\0' ? connect.user : NULL);
gchar *pwd = (connect.password[0] != '\0' ? connect.password : NULL);
gchar *dsn = (connect.db != '\0' ? connect.db : NULL);
SQLRETURN retcode;
gchar stat[10];
SQLINTEGER err;
SQLSMALLINT mlen;
gchar msg[200];

if (dsn == NULL) return 1;

/*Allocate environment handle */
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &inEnv);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
{
return 1;
}

/* Set the ODBC version environment attribute */
retcode = SQLSetEnvAttr(inEnv, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
{
SQLFreeHandle(SQL_HANDLE_ENV, inEnv);
return 1;
}
retcode = SQLAllocHandle(SQL_HANDLE_DBC, inEnv, &connIn);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
{
SQLFreeHandle(SQL_HANDLE_ENV, inEnv);
return 1;
}
/* Set login timeout to 5 seconds. */
SQLSetConnectAttr(connIn, (void*)SQL_LOGIN_TIMEOUT, 5, 0);
retcode = SQLConnect(connIn, (SQLCHAR *) dsn, SQL_NTS, (SQLCHAR *)uid, SQL_NTS, (SQLCHAR *)pwd, SQL_NTS);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
{
SQLGetDiagRec(SQL_HANDLE_DBC, connIn, 1,stat, &err, msg, 100,&amp;amp;amp;mlen);
SQLFreeHandle(SQL_HANDLE_ENV, inEnv);
g_print (_("Connection to DSN '%s' failed.\n"), connect.db);
g_print (_("Error: %s\n"), msg);
return 1;
}
return (0);
}


gint DB_ConnectOutbox (DBConfig connect)
{
gchar *uid = connect.user[0] != '\0' ? connect.user : NULL;
gchar *pwd = connect.password[0] != '\0' ? connect.password : NULL;
gchar *dsn = connect.db != '\0' ? connect.db : NULL;
SQLRETURN retcode;
gchar stat[10];
SQLINTEGER err;
SQLSMALLINT mlen;
gchar msg[200];

if (dsn == NULL) return 1;

/*Allocate environment handle */
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &outEnv);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
{
return 1;
}

/* Set the ODBC version environment attribute */
retcode = SQLSetEnvAttr(outEnv, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
{
SQLFreeHandle(SQL_HANDLE_ENV, outEnv);
return 1;
}
retcode = SQLAllocHandle(SQL_HANDLE_DBC, outEnv, &connOut);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
{
SQLFreeHandle(SQL_HANDLE_ENV, outEnv);
return 1;
}
/* Set login timeout to 5 seconds. */
SQLSetConnectAttr(connOut, (void*)SQL_LOGIN_TIMEOUT, 5, 0);
retcode = SQLConnect(connOut, (SQLCHAR *) dsn, SQL_NTS, (SQLCHAR *)uid, SQL_NTS, (SQLCHAR *)pwd, SQL_NTS);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
{
SQLGetDiagRec(SQL_HANDLE_DBC, connOut, 1,stat, &err, msg, 100,&amp;amp;amp;mlen);
SQLFreeHandle(SQL_HANDLE_ENV, outEnv);
g_print (_("Connection to DSN '%s' failed.\n"), connect.db);
g_print (_("Error: %s\n"), msg);
return 1;
}
return (0);
}


gint DB_InsertSMS (const gn_sms * const data, const gchar * const phone)
{
GString *buf, *phnStr, *maxStr;
SQLRETURN *res;
gchar *text;
SQLHSTMT hstmt;
SQLRETURN retcode;
gchar stat[10];
SQLINTEGER err, msgId, szMsgId;
SQLSMALLINT mlen;
gchar msg[200];

if (phone[0] == '\0')
phnStr = g_string_new ("");
else
{
phnStr = g_string_sized_new (32);
g_string_sprintf (phnStr, "'%s',", phone);
}

text = strEscape (data->user_data[0].u.text);
maxStr = g_string_sized_new (32);
g_string_sprintf (maxStr, "SELECT MAX(id) from inbox");

retcode = SQLAllocHandle(SQL_HANDLE_STMT, connIn, &hstmt);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
{
SQLGetDiagRec(SQL_HANDLE_DBC, connIn, 1, stat,&err, msg, 100, &amp;amp;amp;mlen);
g_print (_("Error: %s\n"), msg);
return 1;
}

retcode = SQLExecDirect(hstmt, (SQLCHAR *) maxStr->str, SQL_NTS);
g_string_free(buf, TRUE);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) {
SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1,stat, &err, msg, 100, &amp;amp;amp;mlen);
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
g_print (_("%d: SELECT MAX(id) from inbox failed.\n"), __LINE__);
g_print (_("Error: %s\n"), msg);
return 1;
}

if ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
SQLGetData(hstmt, 1, SQL_C_ULONG, &szMsgId, 0, &msgId);
++szMsgId;
buf = g_string_sized_new (256);
g_string_sprintf (buf, "INSERT INTO inbox (\"id\", \"number\", \"smsdate\", \"insertdate\", \"text\", %s \"processed\") VALUES ('%s', '%s', '%02d-%02d-%02d %02d:%02d:%02d+01', 'now', '%s', %s 'f')",
phone[0] != '\0' ? "\"phone\"," : "", szMsgId, data->remote.number,
data->smsc_time.year, data->smsc_time.month,
data->smsc_time.day, data->smsc_time.hour,
data->smsc_time.minute, data->smsc_time.second, text, phnStr->str);
g_free (text);
g_string_free(phnStr, TRUE);
retcode = SQLExecDirect(hstmt, (SQLCHAR *) buf->str, SQL_NTS);
g_string_free(buf, TRUE);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) {
SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1,stat, &err, msg, 100, &amp;amp;amp;mlen);
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
g_print (_("%d: INSERT INTO inbox failed.\n"), __LINE__);
g_print (_("Error: %s\n"), msg);
return 1;
}

return (0);
}

void DB_Look (const gchar * const phone)
{
#define DREPORT_LEN 10
#define PHONE_LEN 50
GString *buf, *phnStr;
SQLHSTMT hstmt , hstmt1;
SQLRETURN retcode;
gchar stat[10];
SQLINTEGER err, msgId, szMsgId;
SQLSMALLINT mlen;
gint numError,error;
gchar msg[200];
SQLCHAR szMsg[GN_SMS_MAX_LENGTH], szDReport[DREPORT_LEN], szPhone[PHONE_LEN];
SQLINTEGER destNumber,textMsg, dReport;
if (phone[0] == '\0')
phnStr = g_string_new ("");
else
{
phnStr = g_string_sized_new (32);
g_string_sprintf (phnStr, "AND phone = '%s'", phone);
}

buf = g_string_sized_new (128);

g_string_sprintf (buf, "SELECT id, number, text, dreport FROM outbox WHERE processed='0' AND {fn CURTIME()} >= not_before AND {fn CURTIME()} <= not_after %s", phnStr->str);
g_string_free (phnStr, TRUE);

retcode = SQLAllocHandle(SQL_HANDLE_STMT, connOut, &hstmt);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO))
{
SQLGetDiagRec(SQL_HANDLE_DBC, connOut, 1, stat,&err, msg, 100, &amp;amp;amp;mlen);
g_print (_("Error: %s\n"), msg);
return;
}

retcode = SQLExecDirect(hstmt, (SQLCHAR *) buf->str, SQL_NTS);
g_string_free(buf, TRUE);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) {
SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1,stat, &err, msg, 100, &amp;amp;amp;mlen);
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
g_print (_("%d: SELECT FROM outbox command failed.\n"), __LINE__);
g_print (_("Error: %s\n"), msg);
return;
}

while ((retcode = SQLFetch(hstmt)) == SQL_SUCCESS)
{
gn_sms sms;

gn_sms_default_submit (&sms);
memset (&sms.remote.number, 0, sizeof (sms.remote.number));

SQLGetData(hstmt, 1, SQL_C_ULONG, &szMsgId, 0, &msgId);
SQLGetData(hstmt, 2, SQL_C_CHAR, szPhone, PHONE_LEN, &destNumber);
SQLGetData(hstmt, 3, SQL_C_CHAR, szMsg, GN_SMS_MAX_LENGTH, &textMsg);
SQLGetData(hstmt, 4, SQL_C_CHAR, szDReport, DREPORT_LEN, &dReport);
sms.delivery_report = atoi (szDReport);

if (szPhone != NULL)
strncpy (sms.remote.number, szPhone, sizeof (sms.remote.number) - 1);
else
*sms.remote.number = '\0';
sms.remote.number[sizeof (sms.remote.number) - 1] = '\0';
if (sms.remote.number[0] == '+')
sms.remote.type = GN_GSM_NUMBER_International;
else
sms.remote.type = GN_GSM_NUMBER_Unknown;

if (szMsg != NULL)
strncpy (sms.user_data[0].u.text, szMsg, GN_SMS_MAX_LENGTH + 1);
else
*sms.user_data[0].u.text = '\0';
sms.user_data[0].u.text[GN_SMS_MAX_LENGTH] = '\0';
sms.user_data[0].length = strlen (sms.user_data[0].u.text);
sms.user_data[0].type = GN_SMS_DATA_Text;
sms.user_data[1].type = GN_SMS_DATA_None;
if (!gn_char_def_alphabet (sms.user_data[0].u.text))
sms.dcs.u.general.alphabet = GN_SMS_DCS_UCS2;

gn_log_xdebug ("Sending SMS: %s, %s\n", sms.remote.number, sms.user_data[0].u.text);

numError = 0;
do
{
error = WriteSMS (&sms);
sleep (1);
}
while ((error == GN_ERR_TIMEOUT || error == GN_ERR_FAILED) && numError++ < processed="'1'," error="'%d'," processed_date="NULL" id="'%s'" retcode =" SQLAllocHandle(SQL_HANDLE_STMT," retcode =" SQLExecDirect(hstmt1,">str, SQL_NTS);
g_string_free(buf, TRUE);
if ((retcode != SQL_SUCCESS) && (retcode != SQL_SUCCESS_WITH_INFO)) {
SQLGetDiagRec(SQL_HANDLE_STMT, hstmt1, 1,stat, &err, msg, 100, &amp;amp;amp;mlen);
SQLFreeHandle(SQL_HANDLE_STMT, hstmt1);
g_print (_("%d: UPDATE command failed.\n"), __LINE__);
g_print (_("Error: %s\n"), msg);
return;
}

}
g_string_free (buf, TRUE);

}

Hopefully this code will be usable :D


Tidak ada komentar: