quarta-feira, 6 de agosto de 2014

Caminho relativo em PHP (Relative path)

Recentemente precisei fazer uma manipulação de arquivos em um site que hospedo na Locaweb e me deparei com um problema chato: Só tinha os caminhos absolutos dos arquivos e a locaweb não me dava permissão para acessar o meu arquivo via caminho absoluto.

Explicando em miúdos:
Queria simplesmente renomear um arquivo que estava em um caminho parecido com esse aqui:
/home/storage/userxpto/public_html/pastax/pastay/documentos/documento.doc

Acontece que o meu script PHP estava em um caminho como esse aqui:
/home/storage/userxpto/public_html/sistema_xpto/meuscript.php

Ao executar o comando file_exists(/home/storage/userxpto/public_html/pastax/pastay/documentos/documento.doc) do PHP, sempre recebia um erro por falta de permissão. Daí descobri que não conseguia sequer listar o conteúdo da pasta /home/storage na Locaweb (óbvio que por quest~~oes de segurança da própria Locaweb). A solução foi passar o caminho relativo entre o meu script PHP 'meuscript.php' e o documento 'documento.doc' para o comando 'file_exists'.

Como desconheço uma função em PHP que obtenha o caminho relativo entre dois paths, eu precisei implementar a criança.

Segue o código abaixo:

/***
  getRelativePath obtém o caminho relativo entre dois caminhos de diretórios.
    Ex: $path1 = "/a/b/c/d/e/f/g";
          $path2 = "/a/b/c/d/x/y/z";
          echo "Relative = ".getRelative($path1, $path2);
          //Resultado: ..\..\..\x\y\z
*/
function getRelativePath($path1, $path2){
    $p1 = $p2 = "";

    //Obtendo o ponto de diferença entre os paths
    $i=0;
    while($path1[$i] == $path2[$i]) $i++;

    $p1 = substr($path1, $i);
    $p1 = preg_replace("@[^\\\/]+@", "..", $p1);
    $p1 = preg_replace("@[\\\/]@", DIRECTORY_SEPARATOR, $p1);

    $p2 = substr($path2, $i);
    $p2 = preg_replace("@[\\\/]@", DIRECTORY_SEPARATOR, $p2);

    return $p1.DIRECTORY_SEPARATOR.$p2;
}


Espero que seja útil pra mais alguém!

segunda-feira, 31 de maio de 2010

Criptografia e descriptografia com PHP

Recentemente estava precisando enviar uns dados sigilosos via POST de um formulário para outro. Acontece que isto não pode simplesmente ser feito via um campo escondido, como <input type="hidden"> por exemplo, pois se o usuário mandar o browser mostrar o código fonte da página lá estarão meus dados expostos.
A solução encontrada era encryptar os dados... O problema é que para cryptografia, o PHP só tem funções tipo HASH, cujo conteúdo original é impossível de se recuperar (one-way).
Diante disto tive que implementar minha própria função de criptografia e descriptografia... Sendo assim, me baseei na mais forte delas e, acredite, a mais simples de ser implementada: a criptografia "One-time pad".


Obs: Apesar de ter me baseado na "One-time pad", minha função não exige que a chave tenha o mesmo tamanho que o texto a ser criptografado e nem que seja utilizada uma única vez. Cabe a você, programador, definir seu uso para a mesma.

A função e exemplo de uso vão abaixo:

/* encrypt criptografa e/ou descriptografa uma string qualquer, fornecida no parametro "frase" com uma chave qualquer executando um
    XOR entre cada caractere, invertendo a sequencia e codificando em hexadecimal.
    Se $crypt = true, a função criptografa a frase fornecida. Caso false ela a descriptografa.
     Exemplo de uso:
     $chave = "q6w43a2sc1d6e98r6d5f6dasdfa313d525a35dsf";//Chave a ser utilizada na criptografia/descriptografia
   $frase = "Teste de encriptação de frases!!!";
   $crypt = encrypt($frase, $chave, true);
   $decrypt = encrypt($crypt, $chave, false);
   echo "Frase = ".$frase."<br>";
   echo "Cript = ".$crypt."<br>";
   echo "Decript = ".$decrypt."<br>";
*/
function encrypt($frase, $chave, $crypt)
{
  $retorno = "";

  if ($frase=='') return '';

  if($crypt)
  {
    $string = $frase;
    $i = strlen($string)-1;
    $j = strlen($chave);
     do
    {
      $retorno .= ($string{$i} ^ $chave{$i % $j});
    }while ($i--);

    $retorno = strrev($retorno);
    $retorno = base64_encode($retorno);
  }
  else
  {
    $string = base64_decode($frase);
    $i = strlen($string)-1;
    $j = strlen($chave);

    do
    {
      $retorno .= ($string{$i} ^ $chave{$i % $j});
    }while ($i--);

    $retorno = strrev($retorno);
  }
  return $retorno;
}

segunda-feira, 17 de maio de 2010

Validação de email com PHP: Sintaxe e domínio

Um dia desses um pobre coitado se cadastrou em um dos sistemas que implementei e cadastrou seu e-mail como zemane.com.br. Como você deve ter percebido, o "zé mané" esqueceu (?!?!?!?!) de colocar o domínio do e-mail após a arroba (e esqueceu inclusive a arroba!).
Em suma o cara "esqueceu" de tudo o que é obrigatório em um e-mail e o meu sistema aceitou este dado sem criticar...
Bom.. Para evitar que novos "experts" em fake-e-mail se cadastrassem desta forma, implementei uma funçãozinha que verifica se o texto fornecido tem uma sintaxe válida para ser usado como um endereço de e-mail.

A função é a seguinte:

//isEmailValido verifica se um email está no formato correto (xxx@bbb.ccc). Caso $ignoreempty seja
//false, retornará false se $email for uma string vazia
function isEmailValido($email, $ignoreempty = true)
{
$result = false;

if(strlen(trim($email))==0 && $ignoreempty)
  $result = true;
elseif(preg_match("/^[\w\.-]{1,}\@([\da-zA-Z-]{1,}\.){1,}[\da-zA-Z-]+$/i", $email))
  $result = true;

return $result;
}


Tudo caminhava  muito bem... Mas os problemas não pararam por aí...
Quando o usuário colocava um e-mail com domínio inexistente (por exemplo "meuemail@dominioinexistente.com.br"), o código acima deixava passar numa boa.
Pra isto eu dei uma vasculhada na net, achei diversas funções que se propunham a resolver este problema... Juntei um pedaço de cada um e montei a função abaixo:

/** EmailDomainValid verifica se o domínio do email fornecido existe é válido.
  exemplo de uso: EmailDomainValid( "email@dominio.com.br ").
*/
function EmailDomainValid($email)
{
  if(!preg_match ("/^[\w\.-]{1,}\@([\da-zA-Z-]{1,}\.){1,}[\da-zA-Z-]+$/", $email))
    return false;

  list($prefix, $domain) = explode("@",$email);

  if(function_exists("getmxrr") && getmxrr($domain, $mxhosts))
  {

    foreach($mxhosts as $mx)
    {
      if(@fsockopen($mx, 25, $errno, $errstr, 10))
        return true;
    }
    return false;
  }
  elseif(@fsockopen($domain, 25, $errno, $errstr, 10))
  {
    return true;
  }

  return false;
}

Desde então os problemas com e-mails cadastrados praticamente deixaram de existir :-)

Upload de imagens e redimensionamento das mesmas mantendo transparência quando for o caso

Ainda na linha de gerenciador de conteúdos, precisei, recentemente, implementar uma função que permite fazer upload de uma imagem redimensionando-a para um tamanho determinado (se for o caso), respeitando o efeito de transparência.
Você deve estar se perguntando "como assim, respeitando o efeito de transparência?".
Bom... Acontece que o simples uso da função imageCreateTrueColor (nativa do PHP) destrói a transparência das imagens. Para corrigir isto você deve usar a função imagecolorallocatealpha (nativa do PHP) como poderá ser visto no código abaixo.

A função aguarda um tipo $_FILE dentre outros parâmetros que podem ser entendidos no comentário em seu cabeçalho.
Se você não entende o tipo $_FILE, recomendo a leitura de sua documentação no PHP Net (http://php.net/manual/en/features.file-upload.php) para fins de curiosodade, mas você não precisará entendê-la para usar a minha função. Basta você saber que $_FILE trabalha com post de um input do tipo file, como exemplo abaixo:
<input type="file" name="nomearquivo" value="{default|escape}" size="45">

Ainda, esta função foi desenvolvida para os seguintes formatos: JPG, GIF e PNG. Acredito que para estender seu uso para BMP e outros formatos seja bastante trivial. Se alguém precisar e o fizer, por favor, me dê um retorno para eu atualizar o meu código também :-).
Espero que seja útil a mais alguém.

Lá vai a função:
/*uploadResizedImage faz o upload da imagem $fileOriginal, que é um tipo $_FILE, redimensionando-a
  para a largura e altura máximas informadas ($larguraMaxima, $alturaMaxima) na pasta
  $pastaDeMiniaturas no servidor.
  Obs: Se a imagem original for menor que as novas dimensões fornecidas, a imagem não será
  redimensionada(esta função não estica a imagem).
*/
function uploadResizedImage( $fileOriginal, $nomeMiniatura, $direitorioDestino,
                           $larguraMaxima, $alturaMaxima,
                           $redimensionar = true, $qualidade=95)
{
$extensaoDaFotoMaior = strToLower(end(explode(".", $fileOriginal['name'])));

if($extensaoDaFotoMaior == "jpg") $fimDaFuncao = "Jpeg";
else if($extensaoDaFotoMaior == "gif") $fimDaFuncao = "Gif";
else if($extensaoDaFotoMaior == "png") $fimDaFuncao = "Png";
else return false;

$funcao = "ImageCreateFrom" . $fimDaFuncao;
$imagem = $funcao($fileOriginal['tmp_name']);

$larguraOriginal = ImageSX($imagem);
$alturaOriginal  = ImageSY($imagem);

//imagens jpeg tem qualidade de 0 a 100. Png de 0(no compression) a 9.
//Converto abaixo a porcentagem para o formeto esperado pelo png
if(strcmp($fimDaFuncao,"Png")==0) $qualidade = 9-(9*$qualidade/100);

$larguraNova = $larguraOriginal;
$alturaNova  = $alturaOriginal;

if($redimensionar)
{
  if(($larguraMaxima > 0) && ($larguraMaxima < $larguraNova))
  {
    $alturaNova  = $alturaNova*$larguraMaxima/$larguraNova;
    $larguraNova = $larguraMaxima;
  }

  if(($alturaMaxima > 0) && ($alturaMaxima < $alturaNova))
  {
    $larguraNova = $larguraNova*alturaMaxima/$alturaNova;
    $alturaNova = $alturaMaxima;
  }
}

$novaImagem = imageCreateTrueColor($larguraNova, $alturaNova);

//Lidando com transparencias (apenas png e gif suportam este recurso)
if(($extensaoDaFotoMaior == "png") || ($extensaoDaFotoMaior == "gif"))
{
  imagesavealpha($novaImagem, true);
  $trans_colour = imagecolorallocatealpha($novaImagem, 0, 0, 0, 127);
  imagefill($novaImagem, 0, 0, $trans_colour);
}

imageCopyResampled( $novaImagem, $imagem, 0, 0, 0, 0, $larguraNova,
                    $alturaNova, $larguraOriginal, $alturaOriginal);

$funcao = "Image" . $fimDaFuncao;
$gravou = $funcao($novaImagem, $direitorioDestino . $nomeMiniatura.".".$extensaoDaFotoMaior, $qualidade);

ImageDestroy($novaImagem);
ImageDestroY($imagem);
return $gravou;

quinta-feira, 13 de maio de 2010

Inserção de vídeos em página HTML usando PHP

Recentemente tive um problemão... Precisei desenvolver um gerenciador de conteúdos para um dos sites aqui no trabalho e o mesmo precisava lidar com vídeos... Diversos formatos. Daí começou a minha aventura.
Se você já precisou inserir um vídeo em uma página deve ter percebido que pra cada formato de vídeo você deve usar um código bem particular, diferente... Óbvio se você não quiser usar a tag "embed", que desencorajo totalmente seu uso. Um artigo bem interessante que provavelmente te desencorajará a usar a tag "embed" é este aqui: http://www.maujor.com/tutorial/adeus-embed.php (url 1). Recomendo sua leitura.

Se você quiser entender melhor os formatos de vídeo, quais são recomendados ou não para usar no seu site, visite o site http://websitehelpers.com/video/formats.html (url 2). Me foi de grande ajuda.

Vamos ao que interessa...
Neste artigo me proponho a publicar uma função genérica em PHP , que implementei recentemente, que permite inserir um vídeo, qualquer que seja seu formato, na tua página. Para isto, considerei todos os formatos apresentados na url 2 acima, analisei todos os comentários da url 1 acima, usei o gerador de códigos deste site: http://cit.ucsf.edu/embedmedia/step1.php (url 3) e, para os vídeos em flash no formato flv, usei o player viral, que pode ser obtido em http://www.longtailvideo.com/players/jw-flv-player/ (url 4). Recomendo fortemente você baixar este player-viral e testá-lo antes de prosseguir. Isso o ajudará a fazer ajustes que julgue necessários.

Dependendo do arquivo de entrada, a função retorna o código de inserção de plug-in adequado.
Antes de usar a função, você precisará baixar o player-viral na url 4 acima. Uma vez baixado, crie uma pasta de nome playerviral no local onde os arquivos de vídeos ficarão no seu servidor e copie todos os arquivos do player viral para esta pasta (na verdade os arquivos player-viral.swf, swfobject.js e yt.swf bastam).
Ex: No meu caso, tenho uma pasta no servidor onde ficam todos os meus vídeos: /httpdocs/siteteste/videos. Nesse caso, a pasta playerviral ficará em /httpdocs/siteteste/videos/playerviral.
Obs: Caso você queira trocar a localização e/ou nome desta pasta no teu sistema, altere as linhas indicadas  abaixo convenientemente no código de inserção de plug-in de vídeos FLV:
1) <param name="movie" value="playerviral/player-viral.swf" />
2) <param name="flashvars" value="file=../<?=$filename?>" />
3) src="playerviral/player-viral.swf"
4) flashvars="file=../<?=$filename?>"
Note que nas linhas (2)  e (4), o caminho indicado deve ser a partir da pasta do playerviral para a pasta onde os vídeos flv estiverem armazenados.

Lá vai a função:

function getHTMLVideoCode( $filename, $width=320, $height=320, $allowembed = false)
{
  $extensao = explode(".", $filename);
  $extensao = end($extensao);
  $htmlcode = "";
 
  switch($extensao)
  {
    case "flv": //Funcionando no firefox e no IE. Requer player-viral
      ?>
      <!-- START OF THE PLAYER EMBEDDING TO COPY-PASTE -->
      <!-- A utilização do código abaixo requer que os arquivos do player-viral
           estejam presentes no servidor (pasta playerviral)
      -->
      <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
              name="player" width="<?=$width?>" height="<?=$height?>">
          <param name="movie" value="playerviral/player-viral.swf" />
          <param name="allowfullscreen" value="true" />
          <param name="allowscriptaccess" value="always" />
          <param name="flashvars" value="file=../<?=$filename?>" />
          <embed type="application/x-shockwave-flash"
                 id="player2"
                 name="player2"
                 src="playerviral/player-viral.swf"
                 width="<?=$width?>"
                 height="<?=$height?>"
                 allowscriptaccess="always"
                 allowfullscreen="true"
                 flashvars="file=../<?=$filename?>"
          />
      </object>
      <!-- END OF THE PLAYER EMBEDDING -->
      <?php
      break;
    case "wmv": //Funcionando no firefox e no IE.
    case "wma":   
    case "mp3":   
    case "mpg":   
    case "mpeg":
    case "asf":
      ?>
      <!-- begin embedded WindowsMedia file... -->
      <table border='0' cellpadding='0' align="left">
      <tr><td>
      <OBJECT id='mediaPlayer' width="<?=$width?>" height="<?=$height?>"
      classid='CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95'
      codebase='http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701'
      standby='Loading Microsoft Windows Media Player components...' type='application/x-oleobject'>
      <param name='fileName' value="<?=$filename?>">
      <param name='animationatStart' value='true'>
      <param name='transparentatStart' value='true'>
      <param name='autoStart' value="false">
      <param name='showControls' value="true">
      <param name='loop' value="false">
      <EMBED type='application/x-mplayer2'
        pluginspage='http://microsoft.com/windows/mediaplayer/en/download/'
        id='mediaPlayer' name='mediaPlayer' displaysize='4' autosize='-1'
        bgcolor='darkblue' showcontrols="true" showtracker='-1'
        showdisplay='0' showstatusbar='-1' videoborder3d='-1' width="<?=$width?>" height="<?=$height?>"
        src="<?=$filename?>" autostart="false" designtimesp='5311' loop="false">
      </EMBED>
      </OBJECT>
      </td></tr>
      <!-- ...end embedded WindowsMedia file -->
    <!-- begin link to launch external media player... -->
        <tr><td align='center'>
        <a href="<?=$filename?>" style='font-size: 85%;' target='_blank'>Launch in external player</a>
        </td></tr>
      </table>
      <!-- ...end link to launch external media player... -->
      <?php
      break;
    case "rm": //Funcionando no firefox e no IE. Requer que o usuário tenha o realplayer instalado
      ?>
      <!-- begin embedded RealMedia file... -->
      <table border='0' cellpadding='0' align="left">
        <!-- begin video window... -->
        <tr><td>
        <OBJECT id='rvocx' classid='clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA'
        width="320" height="240">
        <param name='src' value="<?=$filename?>">
        <param name='autostart' value="false">
        <param name='controls' value='imagewindow'>
        <param name='console' value='video'>
        <param name='loop' value="false">
        <EMBED src="<?=$filename?>" width="<?=$width?>" height="<?=$height?>"
        loop="false" type='audio/x-pn-realaudio-plugin' controls='imagewindow' console='video' autostart="false">
        </EMBED>
        </OBJECT>
        </td></tr>
        <!-- ...end video window -->
          <!-- begin control panel... -->
          <tr><td>
          <OBJECT id='rvocx' classid='clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA'
          width="<?=$width?>" height='30'>
          <param name='src' value="<?=$filename?>">
          <param name='autostart' value="false">
          <param name='controls' value='ControlPanel'>
          <param name='console' value='video'>
          <EMBED src="<?=$filename?>" width="<?=$width?>"  height='30'
          controls='ControlPanel' type='audio/x-pn-realaudio-plugin' console='video' autostart="false">
          </EMBED>
          </OBJECT>
          </td></tr>
          <!-- ...end control panel -->
          <!-- ...end embedded RealMedia file -->
      </table>
      <!-- ...end link to launch external media player... -->
      <?php
      break;
    case "mov":
    case "mp4":
      ?>
      <!-- begin embedded QuickTime file... -->
      <table border='0' cellpadding='0' align="left">
        <!-- begin video window... -->
        <tr><td>
        <OBJECT classid='clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B' width="<?=$width?>"
        height="<?=$height?>" codebase='http://www.apple.com/qtactivex/qtplugin.cab'>
        <param name='src' value="<?=$filename?>">
        <param name='autoplay' value="false">
        <param name='controller' value="true">
        <param name='loop' value="false">
        <EMBED src="<?=$filename?>" width="<?=$width?>" height="<?=$height?>" autoplay="false"
        controller="true" loop="false" pluginspage='http://www.apple.com/quicktime/download/'>
        </EMBED>
        </OBJECT>
        </td></tr>
        </table>
        <!-- ...end embedded QuickTime file -->
      <?php
      break;
    default:
      $htmlcode = "<strong>Impossível inserir vídeo $filename: Formato inválido</strong>";
  }
 
  return $htmlcode;
}

Para testar, crie um arquivo php, insira a função acima e o código abaixo:

function cabecalho()
{
  ?>
<html>
<head>
<title>Teste de inserção de vídeos</title>
<meta name="dc.title" content="Embedded Media HTML Generator">
<link rel="StyleSheet" href="/CIT.css" type="text/css">
</head>
<body>
  <?php
}

function rodape()
{
  ?>
  </body>
  </html>
  <?php
}

//$filename = "testes/video.wmv"; //wmv funcionando
//$filename = "video.flv";  //flv funcionando
//$filename = "video.rm"; //rm funcionando
//$filename = "audio.mp3"; //mp3 funcionando
//$filename = "audio.wma"; //wma funcionando
//$filename = "video.mpg"; //wma funcionando
//$filename = "video.mpeg"; //wma funcionando
//$filename = "video.avi"; //wma funcionando
//$filename = "video.mov"; //mov funcionando

$filename = "video.mp4";

echo cabecalho();
echo getHTMLVideoCode( $filename );
echo rodape();


Esta função foi testada para todos os formatos acima listados nos browsers IE8 e Firefox 3.5.8 e funcionou perfeitamente.
Note que aproveitei o embalo e inserí alguns formatos de áudio (wma e mp3). Acredito que se inserir o formato wav funcionará também sem problema algum.

ATENÇÃO: Este arquivo php deve estar na mesma pasta onde se encontram os vídeos! Se você for colocá-lo em outro lugar terá que reajustar os paths que aparecerem nos códigos de cada tipo de mídia convenientemente!

Thats all folks!
Espero que seja útil a alguém :-)

Palavras-chave para facilitar busca no google: PHP inserir vídeos html flv mov mpg avi rm

terça-feira, 5 de janeiro de 2010

Confirmação de email com PHP (com anti sqlinjection e anti-burlagem)

Há 2 anos implementei um sistema de inscrições em PHP de um grande evento onde milhares de congressistas fazem sua inscrição pela internet. No último ano percebi que muitos congressistas tiveram sérios problemas por não terem cadastrado corretamente seus emails(apesar de eu colocar na tela de cadastro um campo de confirmação de email, alguns congressistas têm a capacidade de cadastrar o email errado duas vezes).
Resolvi aplicar a boa e conhecida técnica de enviar um email de confirmação para os congressistas onde os mesmos poderão confirmar que o email que cadastraram é, de fato, válido.

O algoritmo é o seguinte:
1)Assim que o congressista se cadastra no site eu envio um email com uma mensagem do tipo "clique aqui para confirmar o email cadastrado"
2)O congressista recebe o email e clica no link que passo no corpo do email
3)O script feedback.php processa a requisição de confirmação e registra a confirmação no banco de dados (você pode fazer o que quiser, além de registrar no BD)

Para evitar que qualquer um altere a url indiscriminadamente, implemento um código único que vai com a URL. Trata-se de um hash da URL com uma frase secreta. Isto impede ataques de sqlinjection, tentativas de burlagem do sistema, dentre outras coisas.

Você precisará de dois arquivos cujos códigos estão abaixo:
feedback.php: O arquivo que vai processar a confirmação do email quando o internauta clicar no link que receber por email. Aqui você vai personalizar a ação que quer tomar neste processamento.
hash.php: arquivo que gera a codificação anti-sqlinjection e anti-burlagem. A única coisa que você precisa alterar neste arquivo é a frase secreta.

Vamos ao que interessa:

Para enviar o email com o link de confirmação de email:

1)Montando a URL que será passada por email
  Você precisa identificar, na URL, qual o usuário que está querendo confirmar o email. Provavelmente ele tem um ID na tabela onde está cadastrado. Você deve colocar este ID na URL para que saiba qual usuário processar quando o usuário clicar no link.

include_once("hash.php"); //Carregando hasp.php para possibiltar uso da função gerarHashParametros
$idcadastro = 7; //7 é apenas um exemplo. Coloque aqui o id do congressista obtido no bando de dados.
$scriptfeedback = "http://localhost/teste/feedback.php?".gerarHashParametros("idi=".$idcadastro);

Obs: Se você preferir montar a URL dinamicamente, poderá usar o código abaixo em vez do acima. Este código verifica a URL atual e substitui o nome do arquivo php ( e os parâmetros, caso exista, que estiverem sendo passados a ele) por feedback.php acrescido dos parâmetros
Ex: Se a URL atual for http://microsoft/brasil/meuscript.php?<parâmetros do script>
O código abaixo retorna http://microsoft/brasil/feedback.php?<parâmetros gerados por gerarHashParametros("idi=".$idcadastro)>


$scriptfeedback ="http://".$_SERVER["SERVER_NAME"].substr($_SERVER["SCRIPT_NAME"],0, 1+strrpos($_SERVER["SCRIPT_NAME"],"/"))."feedback.php?".gerarHashParametros("idi=".$idcadastro);


2)Eviando a mensagem com o link para o congressista
  O corpo da mensagem deve ser algo como o abaixo:
 

$mailbody = "Prezado(a) congressista,<br><br>O teu cadastro foi registrado com sucesso às ".date("H:i")."
             horas do dia ".date("d/m/Y").".<br><br>
             Para validar o teu email, por favor <a href=\"".$scriptfeedback."\">clique aqui</a> ou
             copie a URL abaixo e cole no teu navegador de internet.<br>".$scriptfeedback."<br><br>";


Aqui você coloca o código para enviar a mensagem acima para o email do usuário(congressista) cadastrado.
Ex: sendSMTPMail( "bill@microsoft.com", "Confirmação de email", $mailbody);
 

////////////////////////////////////////-------------------///////////////////////////////////////////////////////

Código do arquivo feedback.php
include_once("hash.php"); //Carregando hasp.php para possibiltar uso da função validarHashParametros
$mensagem="";

$parametros = validarHashParametros($_SERVER["QUERY_STRING"]);

if (count($parametros)==0)
{
  echo "Violação de segurança. Certifique-se de ter digitado a URL corretamente.
";
  return;
}

if(!isSet($parametros["idi"]))
{
  $mensagem .= "Impossível determinar a inscrição deste congressista. Por favor,
"; 
  $mensagem .= "tente novamente. Caso o problema persista, entre em contato com o webmaster
";
  $mensagem .= "através do fale conosco.";
}
else
{
  //executar aqui o comando que atualizará o teu banco de dados.
  //Exemplo: "update cadastro set emailconfirmado=1 where idcongressista=".$parametros["idi"];

  $mensagem .= "O teu e-mail foi confirmado com sucesso!
Obrigado por manter seus dados atualizados!

";

}
echo $mensagem;
/*Note aqui que a URL gerada, neste exemplo, foi a seguinte:
http://localhost/teste/feedback.php?idi=7&h0p=18f46b7cd89de7701bb1
Se o internauta quiser burlar o sistema e substituir idi=7 por idi=8 (seja qual for a motivação dele), o sistema bloqueará. Experimente!
*/


////////////////////////////////////////-------------------///////////////////////////////////////////////////////

Código do arquivo hash.php
$wordcode = "&¨%-Minha Frase Secreta!-%¨&";
/*Função usada para, a partir de uma lista de parâmetros de URL,
 *gerar um hash entre esta e o código interno ($wordcode) para evitar
 *mal uso dos scripts de processamento de feedbacks dos congressistas
 *de emails enviados pelo sistema.
 Exemplo de uso:
 $parametros = "idi=1775&ide=3&idc=29";
 Produzirá o seguinte link:
 testejob.php?idi=1775&ide=3&idc=29&h0p=bc2e33fb788c5ac049b8
 */
function gerarHashParametros( $parametros, $hashsize=20 )
{
  global $wordcode;
  return $parametros."&h0p=".substr(md5($parametros.$wordcode),0,$hashsize);
}

/*Função usada para analisar o código hash da URL da página atual, verificando se o mesmo é
  válido e gerado segundo o método gerarHashParametros.
  Exemplo de uso:
  $rc = validarHashParametros( $_SERVER["QUERY_STRING"]);
  echo (count($rc)>0)? "Processado com sucesso!
":"HASH INVÁLIDO!
";
*/
function validarHashParametros( $parametros, $hashsize=20 )
{
  global $wordcode;
  $paramoriginal = substr( $parametros, 0, strpos( $parametros, "&h0p="));
  $params        = explode("&", $parametros);
  $paramsvector  = array();
 
  foreach($params as $param)
  {
    $p = explode("=", $param);
    if(count($p)>0) $paramsvector[$p[0]]=$p[1];
  }

  if( isSet($paramsvector["h0p"]) &&
      (strcmp($paramsvector["h0p"], substr(md5($paramoriginal.$wordcode),0,$hashsize))!=0)
    )
  {
    $paramsvector = array();
  }

  return $paramsvector;
}

quinta-feira, 10 de dezembro de 2009

Obtendo os dados de uma tabela no MSSQL (nome e atributos dos campos)

 Em alguns casos é extremamente útil obter os nomes dos campos de uma tabela bem como informações destes campos (tipo, tamanho, se é obrigatório). Considerando estas 4 informações as mais relevantes, posto abaixo um comando do M$SQL para listar tais informações de uma determinada tabela.

----------------------------------------------------------------------------------------------------------------
Listar os campos de uma tabela

basta substituir "nome_da_tabela" pelo nome da Tabela ou View que você queira listar os campos

declare @nometabela VARCHAR(100); SELECT @nometabela = 'nome_da_tabela'
/*Não alterar linhas abaixo*/
SELECT COLUNAS.NAME AS Atributo, TIPOS.NAME AS Tipo,
       COLUNAS.LENGTH AS Tamanho, COLUNAS.ISNULLABLE AS Pode_nulo
FROM SYSOBJECTS AS TABELAS
     INNER JOIN SYSCOLUMNS AS COLUNAS ON (TABELAS.ID = COLUNAS.ID)
     INNER JOIN SYSTYPES   AS TIPOS  ON (COLUNAS.USERTYPE = TIPOS.USERTYPE)
WHERE TABELAS.NAME = @nometabela

Palavras-chave para facilitar busca no google: sql mssql lista campos tabela

Exportar e importar uma consulta em SQL Server para arquivo CSV

Em alguns casos torna-se extremamente necessário exportar o resultado de uma consulta para um arquivo csv, para posterior importação em aplicativos de planilha eletrônica, como o excel por exemplo. Aqui vão os comandos para o caminho de ida (Gerar o CSV a partir de uma consulta sql) e o caminho de volta(que nem é tão volta assim), onde você poderá preencher uma tabela obtendo os dados de um arquivo CSV.

 Exportar uma consulta para arquivo CSV
Obs: Substitua, convenientemente, o nome do arquivo csv, o nome do banco de dados, o nome da tabela e os campos que se quer exportar para o arquivo csv.


declare @sql varchar(8000); select @sql = 'select * from nome_banco_de_dados.dbo.nome_tabela'
declare @filename varchar(255); select @filename = 'c:\nome_do_arquivo.csv'
/*Não alterar linhas abaixo*/
declare @cmd varchar(8000); select @cmd = 'bcp "'+@sql+'" queryout '+@filename+' -c -t; -T '
exec master..xp_cmdshell @cmd
------------------------------------------------------------------------------------------------------------------------------------------------
Importar um arquivo CSV para uma tabela
Obs: Substitua, convenientemente, o nome do arquivo csv, o nome do banco de dados, e o nome da tabela que se quer popular com os dados do arquivo csv.
Obs2: A tabela e o banco de dados já devem existir previamente antes de usar os comandos abaixo.


BULK INSERT nome_banco_de_dados.dbo.nome_tabela
FROM 'c:\nome_do_arquivo.csv'
WITH( FIELDTERMINATOR = ';', ROWTERMINATOR = '\n')
------------------------------------------------------------------------------------------------------------------------------------------------

Espero que seja útil... Já me ajudou muito.

quarta-feira, 9 de dezembro de 2009

PHP com SQL Server 2008

Olá!

Um problema que tive recentemente foi atualizar a versão do PHP e do M$ Sql ao mesmo tempo... O clássico problema dos afoitos... Em vez de atualizar um, ver se tudo funciona e depois atualizar o outro... imperou, como sempre, o "Time is money" e "vamos que vamos"...

Bom... Como era de se esperar, nada mais funcionou... Minhas aplicações não conseguiram mais acessar o banco de dados, etc. e tal.
Pesquisando diversos fóruns percebi que esta solução não é nada fácil de se encontrar... Isto quando os fóruns chegam a ela. Aqui vai o que resolveu o meu problema e espero que resolva o teu.

Uma solução rápida, que parece ter funcionado com muitas pessoas mas não funcionou comigo (soluções simples costumam ter o efeito "lei de Murph" comigo) é o de simplesmente atualizar a dll ntwdblib.dll do teu php. Para tal, basta você baixar a versão 200.80.194.0 do arquivo ntwdblib.dll (clique aqui para tal) e substituir pela que está na tua pasta do PHP (recomendo fortemente fazer backup da tua versão antiga antes desta operação).

Se você consegue se virar bem com os "How to's" da Microsoft, acesse a URL abaixo e boa sorte.
http://www.microsoft.com/downloads/details.aspx?FamilyID=ccdf728b-1ea0-48a8-a84a-5052214caad9&displaylang=en#Instructions

Caso contrário, vou tentar mastigar a solução abaixo.


*)Os passos abaixos devem ser executados na máquina onde o php é executado, isto é, no teu web server.

*)A solução está disponível para os seguintes sistemas operacionais:
  • Windows Server 2003 Service Pack 1
  • Windows XP Service Pack 3
  • Windows Vista
  • Windows Server 2008
  • Windows 7

*)Você precisará baixar o driver do PHP para SQL Server. Faça-o pela URL abaixo:
http://www.microsoft.com/downloads/details.aspx?FamilyID=ccdf728b-1ea0-48a8-a84a-5052214caad9&displaylang=en
ou pela URL abaixo caso a acima não funcione:
http://msdn.microsoft.com/en-us/data/cc299381.aspx

*)Baixe e instale o SQL Native Client de acordo com o teu processador:
Download the X86 package
Download the X64 package
Download the IA64 package
Para tal, acesse a URL abaixo e clique em uma das opções acima listadas.
http://msdn.microsoft.com/en-us/library/cc296170%28SQL.90%29.aspx

*)Após baixar o arquivo SQLServerDriverForPHP11.EXE, execute-o. Será perguntado o local onde os arquivos deverão ser descompactados. Escolha c:\ ou um de sua preferência.
Ao ser descompactado, os seguintes arquivos dll estarão disponíveis:

Arquivo de driver
Versão do PHP
É thread safe?
Usar com este dll
php_sqlsrv_53_nts_vc6.dll
5.3
Não
php5.dll
php_sqlsrv_53_nts_vc9.dll
5.3
Não
php5.dll
php_sqlsrv_53_ts_vc6.dll
5.3
Sim
php5ts.dll
php_sqlsrv_53_ts_vc9.dll
5.3
Sim
php5ts.dll
php_sqlsrv_52_nts_vc6.dll
5.2
Não
php5.dll
php_sqlsrv_52_ts_vc6.dll
5.2
Sim
php5ts.dll

A terminação "ts" indica "thread safe";

A terminação "vc6" arquivo a ser usado se o teu PHP foi compilado pelo Visual C++ 6.0;

A terminação "vc9" arquivo a ser usado se o teu PHP foi compilado pelo Visual C++ 9.0;


*)Mova todos os dll's acima para a pasta de extensões do PHP. Em geral esta pasta tem o nome "ext" e fica dentro da pasta onde ficam os arquivos do PHP. Na minha máquina, por exemplo, fica em c:\xampp\php\ext.

*)Altere o arquivo php.ini, acrescentando a linha abaixo:
extension=<nome_do_arquivo_dll>
substituindo <nome_do_arquivo_dll> pelo nome do arquivo dll conforme tabela acima.
Exemplo: Se o teu PHP foi compilado pelo Visual C++ 6.0 e você deseja usar o driver thread-safe, a linha acrescentada no php.ini deve ser a seguinte:
extension=php_sqlsrv_53_ts_vc6.dll
Obs1: Para saber qual a versão do Visual C++ com a qual o teu PHP está compilado, execute o phpinfo().
Obs2: Acrescentar o extension acima provavelmente significa dizer que você não quer mais usar o driver antigo. Recomendo comentar a linha que carrega o driver antigo (no meu php.ini é a linha "extension=php_mssql.dll").

*)Reinicialize o teu servidor WEB
Obs: Existe uma outra forma de carregar esta extensão sem a necessidade de alterar o arquivp php.ini(e, conseqüentemente, sem a necessidade de reiniciar o servidor web). Para isto, utilize o comando dl no teu script php, conforme exemplo abaixo:
dl('<nome_do_arquivo_dll>');
Veja a documentação do comando dl aqui.


***)Se depois deste árduo trabalho ainda não estiver funcionando, tenho algumas dicas:
  1)Você está usando ADODB? Se sim, certifique-se de estar utilizando a última versão. Baixe a última 
versão aqui.
  2)Na conexão você está usando NewADOConnection("mssql");? Se sim, mude para NewADOConnection("mssqlnative");


Acho que é isso aí.
Quaisquer dúvidas dêem uma consultada em
http://msdn.microsoft.com/en-us/library/cc296173%28SQL.90%29.aspx
que foi a maior fonte de ajuda que tive para resolver o problema e, posteriormente, postar este "tutorial".

Mensagens relacionadas (viabiliza este texto ser encontrado pelo google):
"Unable to connect to server:"

Motivo do blogger...

Apenas alguns "caminhos das pedras"...

Obs: O fato do significado literal do meu nome ser "Pedra de ajuda" é apenas uma coincidência.

Como todo programador, por vezes fico horas procurando alguma solução de problemas/desafios que me aparecem. Em diversas situações não encontro a solução em tais fóruns, mas, ao resolver o problema por mim mesmo, não posso postá-la nos diversos fóruns porque não sou(e nem quero) inscrito em todos eles. Mas me sinto um ingrato quando não publico as soluções que descubro sozinho ou com insights advindos das diversas dicas obtidas nos fóruns. Aqui é uma maneira de me redimir e postar tais soluções... Ao menos estarão publicadas... Cabe às pessoas encontrarem este blogger :-)

Por outro lado...
Tenho estudado freqüentemente para alguns concursos... Tenho feito meus resumos... Mapas mentais... E coletado materiais bem legais. Algumas dicas que tenho recebido (em fóruns, cursos, palestras) também são bastante interessantes. Resolvi postar tudo aqui também... Aos pouquinhos, é claro.

Podem me perguntar... Mas se você teve tanto trabalho para fazer estes resumos... Horas a fio desenhando esses mapas... Porque disponibilizá-los tão gratuitamente para os teus concorrentes?
A resposta vem do mesmo conceito da padronização dos sistemas de gestão empresarial(ERPs): "Não é a informação que faz a diferença, mas o que você faz com ela... Como você a manipula".
Isto partiu de um exemplo de um excelente professor que tive em um dos cursos que fiz em 2009 (Márcio Vitorino): "Se você pegar duas cozinhas exatamente iguais, com os mesmos equipamentos, os mesmos ingredientes e os mesmos temperos e colocar, em cada uma delas, um cozinheiro diferente. Pergunta-se: A qualidade e sabor das comidas serão os mesmos?"
É óbvio que não... Daí o conceito de que 'não são os ingredientes que fazem a diferença, mas como você os manipula.'"