<% function CalcBasePath() { var S = Request.ScriptName; var R = ''; var I = S.length while (I > 0) { if (S.charAt(I) == '/') { R = S.substring(0,I); break; } I--; } return "http://" + Request.Host + R + "/../fb/"; } function GetBasePath() { if (Application.Designing) return Application.QualifyFileName(' '); // relative directory else return CalcBasePath(); // virtual directory } %> Untitled Document

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 números solicitados, usando os recursos básicos do SQL do banco InterBase/Firebird. Após a consulta ser efetuada com sucesso, será gerada uma saída em html, permitindo assim 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/Firebird.
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/Firebird 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ásicos sobre o banco Firebird. Em caso de dúvida na criação do banco, solicite-me via e-mail o banco 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" style="position:absolute; left:34px; top:127px; width:257px; height:19px; z-index:1">
<table border="0" width="278" height="50">
<tr>
<td width="179" height="26" >
<form action="
../cgi-bin/sel_usu" method="get">
<font face="Arial, Helvetica, sans-serif" size="1">NOME</font><font face="Arial, Helvetica, sans-serif">
</font>
<input type="text" name="s_name" size="20">
<input type="submit" value="Buscar" name="submit">
</form>
</td>
</tr>
</table>
</div>
<img src="logo_tel.jpg" width="320" height="195">


Repare que a ação do botão buscar é enviar ao binário sel_usu a variável s_name, que contém o nome a ser pesquisado no banco. O binário deve estar nos diretórios padrão cgi-bin de sua distribuição, e irá receber esta variável como parâmetro .

 


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 construído 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, segue abaixo o link para o download dos fontes.
 Autor: Alessandro Faria