27 de abril de 2007

Envío de correo electrónico por el servidor SMTP de Gmail

Este artículo muestra como enviar un correo electrónico desde Visual FoxPro a través del servidor SMTP de Gmail que utiliza autentificación y cifrado SSL (Secure Sockets Layer).

Gmail permite el envío de correo electrónico utilizando: el servidor smpt.gmail.com, autentificación, cifrado, puerto 465 ó 587, dirección de correo electrónico, nombre de usuario (incluido @gmail.com) y contraseña. (Para mas información de configuración haga clic aquí).

Craig Boyd publicó hace un tiempo en su Blog, varios artículos sobre distintas técnicas para el envio de mails desde VFP. Una de esas técnicas es mediante CDO (Collaboration Data Objects) que nos permite autenficación y cifrado SSL. Tomando como base su artículo "Email and VFP: Part 1c (CDOSYS)", solo se debe cambiar el puerto SMTP a 465 ó 587, y poner a .T. las propiedadades smtpauthenticate y smtpusessl.

El siguiente es el código modificado que nos permite el envió de correo electrónico a través del servidor SMTP de Gmail.
TRY
  LOCAL lcSchema, loConfig, loMsg, loError, lcErr
  lcErr = ""
  lcSchema = "http://schemas.microsoft.com/cdo/configuration/"
  loConfig = CREATEOBJECT("CDO.Configuration")
  WITH loConfig.FIELDS
    .ITEM(lcSchema + "smtpserver") = "smtp.gmail.com"
    .ITEM(lcSchema + "smtpserverport") = 465 && ó 587
    .ITEM(lcSchema + "sendusing") = 2
    .ITEM(lcSchema + "smtpauthenticate") = .T. 
    .ITEM(lcSchema + "smtpusessl") = .T.
    .ITEM(lcSchema + "sendusername") = "usuario@gmail.com"
    .ITEM(lcSchema + "sendpassword") = "contraseña"
    .UPDATE
  ENDWITH
  loMsg = CREATEOBJECT ("CDO.Message")
  WITH loMsg
    .Configuration = loConfig
    .FROM = "usuario@gmail.com"
    .TO = "usuario@hotmail.com"
    .Subject = "Prueba desde Gmail"
    .TextBody = "Este es un mensaje de prueba con CDO con " + ;
      "autenticación y cifrado SSL desde Gmail"
    .Send()
  ENDWITH
CATCH TO loError
  lcErr = [Error: ] + STR(loError.ERRORNO) + CHR(13) + ;
    [Linea: ] + STR(loError.LINENO) + CHR(13) + ;
    [Mensaje: ] + loError.MESSAGE
FINALLY
  RELEASE loConfig, loMsg
  STORE .NULL. TO loConfig, loMsg
  IF EMPTY(lcErr)
    MESSAGEBOX("El mensaje se envió con éxito", 64, "Aviso")
  ELSE
    MESSAGEBOX(lcErr, 16 , "Error")
  ENDIF
ENDTRY
Este código funciona también para las cuentas de correo electrónico brindadas por Google Apps.

[07/04/2015] ACTUALIZACIÓN:

Quizás no puedas enviar correos electrónicos con el código de este artículo dado que Gmail (Google) puede bloquear intentos de inicio de sesión de aplicaciones que no utilicen los últimos estándares de seguridad. Para solucionar este inconveniente, se debe habilitar en nuestra cuenta de Google el acceso a aplicaciones menos seguras. Esto lo haces desde el enlace: https://www.google.com/settings/security/lesssecureapps


Hasta la próxima,

Luis María

19 de abril de 2007

Proteger DBC en Red

Bueno hoy vi un articulo mas sobre como proteger nuestras DBC y por ende las DBF en la RED sobre usuarios caseros. Siempre tuve ese temor de que alguien pudiera con un simple USE cambiar o modificar la información de mi data...

... Felizmente solo una vez tuve ese problema y pude darme cuenta a tiempo, hasta esa fecha siempre tuve usuarios que desconocian netamente lo que era VFP y nunca se imaginaban lo sencillo que es modificar los datos en la RED.

Soñe siempre con publicar un articulo netamente de ayuda en codigo VFP para ayudar a la comunidad pero no se me pudo cumplir esa meta.

Hoy les comento que encontré un programa: Universal Shield 4.1 que me ayudo a ocultar mis carpetas y que solo se pudiera tener acceso al ejecutable de mi sistema por medio de un acceso directo el mismo que lo voy pegando en las estaciones PCs. A mi particularmente hasta el momento me ha dado grandes satisfacciones este programa ya que tengo todo oculto en mi "servidor" en el cual tampoco se puede ver la carpeta del sistema (buena hay una forma), pero por RED hasta ahora no he visto que alguien pueda entrar a hurgar en mis DBF.


Les comento algo del programa:
  • Su instalación es de lo mas sencilla.
  • Acepta poner un password al acceso del programa.
  • Se elige la carpeta y subcarpetas a ocultar: en carpeta se elige la opcion lectura y escritura, en la subcarpeta se elige acceso total.
  • Existen programas similares pero que aun no los he revisado.
  • Como les comente por medio de la RED no encontré la forma de poder ver la carpeta de mi SISTEMA.
  • Se pueden dar otras opciones al programa para otro nivel de acceso, eso los dejo a investigacion suya.
Mi nombre es Danyer Valencia LLamoca de Ilo, Perú, y me dedico al FOX desde su versión 6.0 y para mi también es lamentable la no continuidad de Visual FoxPro.

Bueno espero que este programa les sea de ayuda a la comunidad

Descarga: Universal Shield 4.1 (1.76 MB).

Danyer Valencia LLamoca

17 de abril de 2007

Evitar la apertura de tablas contenidas en un DBC

Una gran falencia de VFP es el tema de la seguridad de los datos contenidos en su tablas nativas. Muchas veces queremos al menos evitar que con un simple USE los usuarios inexpertos curioseen nuestras tablas. Aquí un simple truco.

A partir de VFP7, podemos habilitar los eventos en los contenedores de bases de datos. Uno de los eventos que podemos controlar es el dbc_BeforeOpenTables, en el cual retornando un valor .F. nos impide abrir una tabla o vista contenida en un DBC.

En primer lugar debemos habilitar los eventos de nuestra base de datos, esto se logra mediante la función:
DBSETPROP("MiDBC", "DATABASE", "DBCEvents", .T.)
O también desde el menú Base de datos -> Propiedades, y marcando el cuadro de verificación "Activar eventos"

Una vez habilitados los eventos, debemos crear el procedimiento almacenado dbc_BeforeOpenTable y colocar el siguiente código:
PROCEDURE dbc_BeforeOpenTable(cTableName)
  LOCAL lcPass
  * En este ejemplo la "clave" la colocamos directamente
  * en el código, pero podemos tomarla de un archivo de texto,
  * una clave del registro de Windows, etc.
  lcPass = "PASSword"
  IF VARTYPE(gc_P_A_S_S) # "C" OR gc_P_A_S_S # lcPass
    PUBLIC gc_P_A_S_S 
    gc_P_A_S_S = INPUTBOX("Ingrese contraseña", "Aviso", "") 
  ENDIF 
  RETURN gc_P_A_S_S == lcPass 
ENDPROC
Este código interroga sobre el tipo y el valor de una variable pública; y si no coincide con nuestra clave nos presenta un cuadro de diálogo para ingresarla.

Ingresar contraseña

En caso que el valor ingresado con coincida, no se permitirá la apertura de la tabla.

Acceso denegado

Para evitar que se visualicen estos cuadros de diálogos en nuestras aplicaciones, debemos definir una variable Pública llamada gc_P_A_S_S y darle el valor de la clave elegida antes de intentar alguna tabla.

Debemos aclarar que esto no es una seguridad real para nuestros datos, ya que un usuario con pocos conocimientos podrá deshabilitar los eventos de nuestra base de datos con una simple línea de código como:
DBSETPROP("MiDBC", "DATABASE", "DBCEvents", .F.)
O simplemente averiguar la clave o modificar el procedimiento almacenado de la base de datos con:
MODIFY PROCEDURE

*** ACTUALIZACION ***
Como indica DragonNegro en su comentario, solo basta añadir el siguiente código en el evento OpenData para evitar el uso de los comandos MODIFY PROCEDURE ó DBSETPROP("MiDBC", "DATABASE", "DBCEvents", .F.).
PROCEDURE dbc_OpenData(cDatabaseName, lExclusive, lNoupdate, lValidate)
  *Immediately after DBC is opened.
  LOCAL lcPass
  lcPass = "PASSword" && Esta es la contraseña
  IF VARTYPE(gc_P_A_S_S) # "C" OR gc_P_A_S_S # lcPass
    PUBLIC gc_P_A_S_S
    gc_P_A_S_S = INPUTBOX("Ingrese contraseña para la base de datos: " ;
      + UPPER(JUSTFNAME(cDatabaseName)), "Aviso", "")
  ENDIF
  RETURN gc_P_A_S_S == lcPass
ENDPROC
*** FIN ACTUALIZACION ***

Aclaramos nuevamente que esto no es seguridad en nuestras tablas, sino una forma de complicar un poco su acceso. No podemos obtener una auténtica seguridad en nuestros datos sobre tablas nativas de Visual FoxPro, para ello debemos migrar los datos a otro motor de bases de datos como SQL Server o MySQL por ejemplo.

Hasta la próxima.

Luis María Guayán

16 de abril de 2007

¿Cuál es el ancho de una lista en un combo?

Artículo original: What’s the width of a combo list
http://www.foxpert.com/knowlbits_200703_3.htm
Autor: Christof Wollenhaupt
Traducido por: Ana María Bisbé York


Si tiene un combobox para el que no ha especificado un valor de la propiedad ColumnWidths, Visual FoxPro tiene que calcular por si mismo cuál podría ser el ancho de la columna desplegada. Básicamente, Visual FoxPro simplemente crea la lista con el ancho suficiente para mostrar completamente todos los valores. Sin embargo, Visual FoxPro solamente toma parte de la lista para determinar el ancho real de cada línea. El siguiente código de ejemplo demuestra este comportamiento:
Public goForm, gaData[121]
goForm = CreateObject("Form")
goForm.Show()
For lnRow=1 to 120
  gaData[lnRow] = "_"+Transform(m.lnrow)
EndFor
gaData[121] = "AAAAAAAAAAAAAAA*"
* first combo
goForm.AddObject("c1","combobox")
goForm.c1.RowSource = "gaData"
goForm.c1.RowSourceType = 5
goForm.c1.ListIndex = 1
goForm.c1.Visible = .T.
* second combo
goForm.AddObject("c2","combobox")
goForm.c2.RowSource = "gaData"
goForm.c2.RowSourceType = 5
goForm.c2.ListIndex = 100
goForm.c2.Top = 50
goForm.c2.Visible = .T.
Ambos comboboxes están configurados con idéntico origen (RowSource). La única diferencia entre los dos, es el elemento actualmente seleccionado. El primer combo muestra el primer elemento, el segundo muestra un elemento que está cercano del final. Si abre ahora ambos comboboxes, verá que el primero es más estrecho y no mostrará el último elemento en toda su longitud.

VFP utiliza el elemento actual y luego lee una cantidad variable de líneas. En el ejemplo de arriba siempre es 68 en mi PC. Esto es, con un ListIndex de 53, el combobox se comporta como el primero, con un ListIndex de 54 se muestra como el segundo. En otros casos, encontré que la cantidad de líneas puede ser mucho más que 10, poniendo la línea grande fuera del área visible de las 7 líneas que muestra de forma predeterminada.

2 de abril de 2007

Los dos motores

Artículo original: The Two Engines
http://www.foxpert.com/knowlbits_200702_6.htm
Autor: Christof Wollenhaupt
Traducido por: Ana María Bisbé York


Muchas bases de datos tienen optimizadores; pero Visual FoxPro tiene dos. Usualmente trabajan de forma similar pretendiendo ser un motor poderoso. Sin embargo, a veces, necesita saber las diferencias entre los dos motores para escribir un código eficiente. Los motores de los que hablo son los motores de optimización xBase y SQL Rushmore.

Una de las diferencias es que econstituye una expresión que puede ser optimizada. El archivo ayuda dice que las expresiones tienen una de las formas siguientes:
eIndex relOp eExp
o
eExpr relOp eIndex
En otras palabras, las dos expresiones siguientes son optimizables:
Name = "Smith"
"Smith" = Name
Si usted analiza qué ocurre cuando ejecuta una consulta con esta condición verá que existen diferencias entre utilizar la cláusla FOR en un comando tradicional y en la cláusula WHERE en una sentencia SQL. Un comando con una cláusula FOR ejecuta mucho más rápido. Está totalmente optimizada.

La sentencia SQL, por otra parte, muestra una barra de progreso de la misma consulta. SYS(3054), la función utilizada para determinar el nivel de optimización, indica que la consulta no está optimizada del todo.

En otras palabras: En XBase no importa dónde escriba el nombre del campo. En SQL, sin embargo, no tiene gran problema. Para evitar una confusión, es una buena idea que utilice siempre la notación primero con el nombre del campo.