Infra - Linux

Biblioteca VBMcgi: Crie aplicações Web CGI em C++ com acesso ao Bco Interbase/Firebird sem MISTÉRIO

VBMcgi é uma biblioteca multiplataforma para desenvolvimento de aplicações CGI, 100% open-source e muito fácil de ser utilizada. Com este recurso podemo cria aplicações Web usando todo os recursos da linguagem C++ sem misticismo!

por Alessandro de Oliveira Faria



VBMcgi é uma biblioteca multiplataforma para desenvolvimento de aplicações CGI, 100% open-source e muito fácil de ser utilizada. Com este recurso podemos criar aplicações Web usando todos os recursos da linguagem C++ sem misticismo!

Introdução:

Neste documento, criaremos um CGI que consulta em uma tabela de telefone os numeros solicitados usando os recursos básico do SQL do banco interbase. Após a consulta ser efetuada com sucesso, será gerada uma saída em html, assim permitindo a consulta via web.

Download e instalação:

O download encontra-se no link http://www.vbmcgi.org/VBMcgi_unix_30.zip testado em linux com o compilador gnu C++ 3.2, e também em http://www.vbmcgi.org/VBMcgi_win_30.zip testado em windows com o compilador Microsoft Visual C++ 6 Service Pack 5.

Após o download, crie uma pasta e efetue a descompactação do arquivo obtido, compilação e instalação executando os seguintes comandos:

[root@p233 /]# mkdir lib_cgi
[root@p233 /]# cd lib_cgi
[root@p233 lib_cgi]# wget http://www.vbmcgi.org/VBMcgi_unix_30.zip 
[root@p233 lib_cgi]# unzip VBMcgi_unix_30.zip
Archive: VBMcgi_unix_30.zip
inflating: vbmcgi_global.cpp
inflating: vbmcgi_VBMenuHtml.cpp
inflating: vbmcgi_VBPageCount.cpp
inflating: vbmcgi_VBTableMonth.cpp
inflating: make.sh
inflating: vbmcgi.so
inflating: vblib.h
inflating: vbmcgi.h
inflating: vbmcgi_VBMenuHtml.h
inflating: vbmcgi_VBPageCount.h
inflating: vbmcgi_VBTableMonth.h
inflating: vblib.cpp
inflating: vbmcgi.cpp
[root@p233 lib_cgi]# ./make.sh
Rebuilding the VBMcgi library 3.0. see http://www.vbmcgi.org
compiling static VBMcgi
binding static VBMcgi
bilding dynamic VBMcgi

Neste momento, acabamos de criar a biblioteca VBMcgi versão estática e dinâmica (libvbmcgi.a e vbmcgi.so). Agora copie estes dois arquivos para um dos diretórios encontrado no arquivo /etc/ld.so.conf ou inclua o path das bibliotecas VBMcgi no arquivo ld.so.conf.

Se você optou em alterar o arquivo ld.so.conf, não esqueça de utilizar o comando ldconfig para atualizar o arquivo ld.so.cache. No meu caso decidi copiar as bibliotecas para o path padrão:

[root@p233 lib_cgi]# cp libvbmcgi.a /usr/local/lib/
[root@p233 lib_cgi]# cp vbmcgi.so /usr/local/lib/

Neste ponto, temos a nossa biblioteca VBMcgi instalada e pronto para ser usada.

Criando o banco de dados:

Não é o objetivo deste tutorial explicar a instalação do banco de dados firebird ou interbase.

A instalação do firebird não tem segredo, pois após o comando rpm, tudo estará devidamente configurado e instalado. Agora se voce deseja instalar o interbase por algum motivo, será encontrado todos os procedimento de instalação deste pacote em http://www.vivaolinux.com.br/artigos/verArtigo.php?codigo=180

Segue abaixo o metadata do banco de dados que usaremos neste tutorial. Crie um banco dados com a estrutura abaixo:

SET SQL DIALECT 1;

/* CREATE DATABASE "servidor:/servdad/secretaria.gdb" PAGE_SIZE 1024

DEFAULT CHARACTER SET NONE */

/* Table: TELEFONES, Owner: SYSDBA */

CREATE TABLE TELEFONES 
(
CODIGO INTEGER,
NOME VARCHAR(60),
DDD VARCHAR(2),
TELEFONE VARCHAR(8),
TIPO VARCHAR(15)
);

/* Index definitions for all user tables */

CREATE INDEX NOME ON TELEFONES(NOME);
CREATE GENERATOR SEQ_TELEFONE;
SET TERM ^ ;

/* Triggers only will work for SQL triggers */

CREATE TRIGGER SEQ_TELEFONE FOR TELEFONES 
ACTIVE BEFORE INSERT POSITION 0
AS BEGIN
NEW.CODIGO = GEN_ID (SEQ_TELEFONE, 1);
END
^
COMMIT WORK ^
SET TERM ;^

Volto a mencionar que este tutorial não contempla conceitos básico sobre o banco firebird. Em caso de dúvida na criação do banco, me solicite via e-mail o bco de dados em branco ou consulte na internet como executar a criação do mesmo.

Interface em HTML:

Agora criaremos uma interface em html cujo o objetivo é de enviar para a aplicação CGI, o nome a ser consultado no banco de dados. Após a consulta o CGI irá gerar um o resultado em html. Segue abaixo o código em html:

<body leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
<div id="Layer1">

Segue abaixo o resultado visual deste tutorial:

Criando o CGI em C++:

Abaixo encontra-se o código fonte CGI em C++. Em virtude da extensão do código fonte, irei disponibiliza-los para download no fim deste artigo. Para maiores detalhes no aspecto de aprendizado, consulte a documentação do Firebird/Interbase e da biblioteca VBMcgi.

O código abaixo é bem simples. Após receber a variável s_name da interface HTML, O select é executado no banco de dados usando a cláusula where do sql. Concluindo esta operação, o resultado é exibido em html construido pela nossa aplicação.

#include "vbmcgi.h"

 
#include "ibase.h"
#include "example.h"
#include <fstream>
#include <iostream>
#include <strstream>

 
using namespace std;

 
#define REGILEN 5
#define NOMELEN 60
#define DDDLEN 2
#define TELEFONELEN 8
#define TIPO 15
#define BUFLEN 512
#define SQL_VARCHAR(len) struct {short vary_length; char vary_string[(len)+1];}

 
int main(void)
{ 
VBMcgi cgi;
cgi.httpCompleteHeader(); 
cgi.formDecode(); 

 
string mTmpINIstr;
 
char registro[REGILEN + 2];
SQL_VARCHAR(NOMELEN) nome;
SQL_VARCHAR(DDDLEN) ddd;
SQL_VARCHAR(TELEFONELEN) telefone;
SQL_VARCHAR(TIPO) tipo;
string mNome;
string mDDD;
string mTelefone;
string mTipo;
 
 
ISC_QUAD blob_id;
isc_blob_handle blob_handle = NULL;
short blob_seg_len;
char blob_segment[512];
isc_db_handle DB = NULL; 
isc_tr_handle trans = NULL; 
long status[20]; 
isc_stmt_handle stmt = NULL; 
XSQLDA ISC_FAR * sqlda;
long fetch_stat, blob_stat;
short flag0 = 0,flag1 = 0;
char empdb[128];
char user_name[31];
char password[31];
char sel_str[256];
char ISC_FAR * dpb = NULL, *d, *p, *copy;
short dpb_length = 0;
long l,sweep_interval = 16384;
int grid;
string mHTML,mSQL;
VBString str_name = cgi.getVarContent("s_name"); 
strcpy(sel_str,"SELECT codigo as registro,nome,ddd,telefone,tipo FROM telefones ");
strcat(sel_str,"WHERE nome LIKE "%"); 
strcat(sel_str,str_name.c_str() );
strcat(sel_str,"%" ORDER BY nome");

 
copy = dpb = (char *) malloc(7);
p = dpb;
*p++ = "\1";
*p++ = isc_dpb_sweep_interval;
*p++ = "\4";
l = isc_vax_integer((char ISC_FAR *) &sweep_interval, 4);
d = (char *) &l;
*p++ = *d++;
*p++ = *d++;
*p++ = *d++;
*p = *d;
dpb_length = 7;
 
strcpy(user_name, "SYSDBA");
strcpy(password, "masterkey");
 
isc_expand_dpb(&dpb, (short ISC_FAR *) &dpb_length, isc_dpb_user_name, user_name, isc_dpb_password, 
password, NULL);

 
mTmpINIstr = "192.168.0.1:/servdad/secretaria.gdb";
strcpy(empdb,mTmpINIstr.c_str());
 
if (isc_attach_database(status, 0, empdb, &DB, dpb_length, dpb)) isc_print_status(status);

 
if (isc_start_transaction(status, &trans, 1, &DB, 0, NULL))
{
ERREXIT(status, 1)
}
 
sqlda = (XSQLDA ISC_FAR *) malloc(XSQLDA_LENGTH(5));
sqlda->sqln = 5;
sqlda->sqld = 0;
sqlda->version = 1;

 
if (isc_dsql_allocate_statement(status, &DB, &stmt))
{
ERREXIT(status, 1)
}
 
if (isc_dsql_prepare(status, &trans, &stmt, 0, sel_str, 1, sqlda))
{
ERREXIT(status, 1)
}

 
sqlda->sqlvar[0].sqldata = (char ISC_FAR *)registro;
sqlda->sqlvar[0].sqltype = SQL_TEXT + 1;;
sqlda->sqlvar[0].sqlind = &flag0;
 
sqlda->sqlvar[1].sqldata = (char *)&nome;
sqlda->sqlvar[1].sqltype = SQL_VARYING + 1;
sqlda->sqlvar[1].sqlind = &flag1;

 
sqlda->sqlvar[2].sqldata = (char *)&ddd;
sqlda->sqlvar[2].sqltype = SQL_VARYING + 1;
sqlda->sqlvar[2].sqlind = &flag1;

 
sqlda->sqlvar[3].sqldata = (char *)&telefone;
sqlda->sqlvar[3].sqltype = SQL_VARYING + 1;
sqlda->sqlvar[3].sqlind = &flag1;

 
sqlda->sqlvar[4].sqldata = (char *)&tipo;
sqlda->sqlvar[4].sqltype = SQL_VARYING + 1;
sqlda->sqlvar[4].sqlind = &flag1;
 
if (isc_dsql_execute(status, &trans, &stmt, 1, NULL))
{
ERREXIT(status, 1)
}

 
std::cout << "<HTML><body>" << std::endl;

 
std::cout<<"<table border="0" width="100%" height="46">"<<std::endl;
std::cout<<" <tr>"<<std::endl;
std::cout<<" <td align="center" colspan="5" bgcolor="#FF9900"><b>"<<std::endl;
std::cout<<" <font color="#FFFFFF" face="Tahoma" size="2">NETi TECNOLOGIA - Agenda Telefonica 
</font></b></td>"<<std::endl;
std::cout<<" </tr>"<<std::endl;
std::cout<<" <tr>"<<std::endl;
std::cout<<" <td width="6%" align="center"><b><font face="Tahoma" 
size="1">Registro</font></b></td>"<<std::endl;
std::cout<<" <td width="65%" align="center"><b><font face="Tahoma" 
size="1">Nome</font></b></td>"<<std::endl;
std::cout<<" <td width="2%" align="center"><b><font face="Tahoma" 
size="1">DDD</font></b></td>"<<std::endl;
std::cout<<" <td width="12%" align="center"><b><font face="Tahoma" 
size="1">Telefone</font></b></td>"<<std::endl;
std::cout<<" <td width="15%" align="center"><b><font face="Tahoma" 
size="1">Tipo</font></b></td>"<<std::endl;
std::cout<<" <tr>"<<std::endl;
 
grid = 0; 
while ((fetch_stat = isc_dsql_fetch(status, &stmt, 1, sqlda)) == 0)
{
registro[sqlda->sqlvar[0].sqllen] = "\0";

 
mNome = nome.vary_string;
if (grid==0)
{ mHTML = "<TD>";
grid = 1;
} 
else
{mHTML = "<TD bgcolor="#FFECC6">";
grid = 0;}
 

 
std::cout<<mHTML<<"<font face="Tahoma" 
size="1">"<<registro<<"</font></TD>"<<std::endl;
std::cout<<mHTML<<"<font face="Tahoma" size="1">";
printf("%-60.*s ", nome.vary_length, nome.vary_string);
std::cout<<"</font></TD>"<<std::endl;

 
std::cout<<mHTML<<"<font face="Tahoma" size="1">";
printf("%-2.*s ", ddd.vary_length, ddd.vary_string);
std::cout<<"</font></TD>"<<std::endl;

 
std::cout<<mHTML<<"<font face="Tahoma" size="1">";
printf("%-8.*s ", telefone.vary_length, telefone.vary_string);
std::cout<<"</font></TD>"<<std::endl;

 
std::cout<<mHTML<<"<font face="Tahoma" size="1">";
printf("%-15.*s ", tipo.vary_length, tipo.vary_string);
std::cout<<"</font></TD><tr>"<<std::endl;
 
 
}

 
if (fetch_stat != 100L)
{
ERREXIT(status, 1)
}

 
if (isc_dsql_free_statement(status, &stmt, DSQL_close))
{
ERREXIT(status, 1)
}

 
if (isc_commit_transaction (status, &trans))
{
ERREXIT(status, 1)
}

 
if (isc_detach_database(status, &DB))
{
ERREXIT(status, 1)
}

 
free(sqlda);

 
std::cout<<"</table>"<<std::endl;
std::cout<<"</body>"<<std::endl;
std::cout<<"</HTML>"<<std::endl;

 
return 0;
}

Para compilarmos o programa, basta executar o seguinte comando abaixo:
[root@p233 cgi]# cg++ sel_usu.cpp -o sel_usu -lvbmcgi -lgds -ldl -lcrypt

As bibliotecas a serem utilizadas exceto a vbmcgi, deverão variar de acordo como a versão do banco de dados instalado. Para maiores informações, consulte a documentação.

Após a compilação, devemos copiar os arquivos select.html e logo_tel.jpg (imagem) para o diretório default do apache e o binário sel_usu para o diretório cgi-bin.

Não esqueça de ajustar o caminho do banco de dados no fonte, o IP do servidor e a senha do SYDBA caso não seja masterkey.

Para facilitar a execução deste tutorial, clique aqui para o download dos fontes.

Alessandro de Oliveira Faria

Alessandro de Oliveira Faria - Sócio-proprietário da empresa NETi TECNOLOGIA fundada em Junho de 1996 (http://www.netitec.com.br), empresa especializada em desenvolvimento de software e soluções biométricas, Consultor Biométrico na tecnologia de reconhecimento facial, atuando na área de tecnologia desde 1986 assim propiciando ao mercado soluções em software nas mais diversas linguagens e plataforma, levando o Linux a sério desde 1998 com desenvolvimento de soluções open-source, membro colaborador da comunidade Viva O Linux, mantenedor da biblioteca open-source de vídeo captura entre outros projetos.