unit urosx;
{$MODE Delphi}
{$DEFINE PROXY_ENABLED_ROS}

interface

uses
  Classes, SysUtils,
  uauxiliares,
  {$IFNDEF LINUX}
 Winsock,
  {$ELSE}
 unix,
 netdb,
 sockets,
  {$ENDIF}
  {$IFDEF PROXY_ENABLED_ROS}
  httpsend,
  {$ENDIF}
  //  synacode,
  //  uglobsharedmem,
  //  base64, Dialogs,
  uDataSetGenerico, ufields,
  uDBConBase;

const
  MSK_USUARIO_COMUN = 1; // tiene permisos para bajar.
  MSK_USUARIO_ADMIN = 2; // tiene permisos para subir.
  MSK_USUARIO_AGENTE = 4; // puede leer la información restringida a los agentes.
  MSK_USUARIO_ROOT = 8; // YO

type
  TTipoServidor = (CTS_PostgreSQL, CTS_MySQL);

type
  TDAOfByte = array of byte;
  TDBrosxCon = class;

  { TResultadoQuery }

  TResultadoQuery = class(TDataSetGenerico)
    dbcon: TDBrosxCon;
    pasaporte: string;
    nid: integer;
    error: string;

    constructor CreateQuery(dbcon: TDBrosxCon; sql: string;
      maxNReintentos: integer = 0);
    constructor CreateExec(dbcon: TDBrosxCon; sql: string);
    procedure call_ros_constructor_helper(dbcon: TDBrosxCon;
      orden: string; ParamStr: string; maxNReintentos: integer = 0);

    //Realizan Login para obtener un pasaporte
    constructor CreateLogin(dbcon: TDBrosxCon; usr, clv: string);
    //Elimina el pasaporte
    constructor CreateLogout(dbcon: TDBrosxCon; pass: string);

    //Se requiere pasaporte para realizar las acciones con pass
    constructor CreateNubeQuery_pass(dbcon: TDBrosxCon;
      pass, nid, nid_version, Data: string);
    constructor CreateNubeExec_pass(dbcon: TDBrosxCon;
      pass, nid_version, nid_data, nBloque, Data, permiso: string);

    procedure call_ros_constructor_helper_pass(dbcon: TDBrosxCon;
      orden: string; pass, nid, nid_version, nBloque, Data, permiso: string);

    procedure call_ros_constructor_helper_login(dbcon: TDBrosxCon;
      orden: string; usr, clv: string);

    procedure call_ros_constructor_helper_logout(dbcon: TDBrosxCon;
      orden: string; pass: string);
  end;

  { TDBrosxCon }

  {$IFDEF SININTERFASES_FEDE}
  TDBrosxCon = class //(IDBConBase)
  {$ELSE}
  TDBrosxCon = class(IDBConBase)
  {$ENDIF}
    // en caso de ocurrir en un error, se guarda en esta variable
    // el mensaje de error correspondiente.
    ultimoError: string;

    // Se carga al conectarse
    tipoServidor: TTipoServidor;

    // estas varibles queda fijadas una vez que el usuario se logea correctamente
    usuario_loginok: boolean;
    usuario_tipo: integer;
    usuario_nid: integer;
    usuario_email: string;
    usuario_grupo: integer;
    usuario_EsAdmin, usuario_EsRoot: boolean;

    // servidor http
    ipfija: string; // si se inicializa no se consulta la ip
    host: string; // se usa si ipfija='' para buscar la ip
    puerto: string; // por defecto es 80
    uri_rosx: string;

    {$IFDEF PROXY_ENABLED_ROS}
    // se usan solo si está en modo  PROXY_ENABLED_ROS
    proxy_host, proxy_port, proxy_user, proxy_pass: string;
    {$ENDIF}

    Usuario_http, Clave_http: string;


    constructor Create(
       ipfija: string;
       host: string;
       puerto: string;
       uri_rosx: string;
       usuario_http: string = '';
       clave_http: string = '' );

    // determina el tipo de servidor en base a una consulta SELECT version()
    // y lo deja en la variable tipoServidor
    procedure determinar_tipo_servidor;

    // si el resultado es true se logró ejecutar con exito.
    // si es false, en ultimoError está el mensaje de error.
    function sql_exec(sql: string): boolean;

    // Si la consulta tiene éxito se retorna el resultado
    // si falla la consulta, se retorna NIL y en UltimoError está el mensaje de error.
    function sql_query(sql: string; maxNReintentos: integer = 0): TResultadoQuery;

    // Si la consulta tiene éxito se retorna el resultado.
    // si falla la consulta, se retorna NIL y en UltimoError está el mensaje de error.
    //Es igual que la funcion sql_query pero se le pasa esplicitamente los parametros
    //que recibe el rosx.php y la consulta se construye en el php
    //si nid=0, se trae el ultimo registro subido en la tabla nube_simsee
    //Si se trae algo en data, buscará en la tabla todos los registros que contengan ese data.
    function sql_query_pass(pass: string; nid, nid_version: integer;
      Data: string = ''): TResultadoQuery;

    // si el resultado es >0 se logró ejecutar con exito y dicho numero
    //corresponde al nid_archivo del item que se insertó (no es el nid).
    // si es -1, en ultimoError está el mensaje de error.
    //Es igual que la funcion sql_exec pero se le pasa esplicitamente los parametros
    //que recibe el rosx.php y la consulta se construye en el php
    function sql_exec_pass(pass, Data: string;
      nid_version, nid_data, nBloque: integer; permiso: string): integer;

    //realiza el login en la tabla nube_simsee
    function sql_login(usr, clv: string): string;

    //realiza el logout en la tabla nube_simsee
    function sql_logout(pass: string): boolean;

    // hace la consulta y retorna un string con el resultado
    // si es error retorna '' El string vacío.
    // Esta llamada es últil para funciones que retornan un sólo valor
    function sql_func(sql: string; maxNReintentos: integer = 0): string;

    // hace la consulta y retorna el record (FICHA) correspondiente
    // si da error retorna nil. El usuario debe llamar al FREE de la ficha
    // Es útil para las consultas en las que se quiere trabajar sobre un único record
    function sql_ficha(sql: string): TDataRecord;

    // retorna el siguiente valor de la tabla sequencias
    function sql_nextnid(nombre: string): integer;
    function sql_nextnid_pass(nombre, pass: string): integer;
    function sql_nextnidarchivo_pass(nombre, pass: string): integer;
    function nextval(nombre: string): integer;


    // Retorna un string con la hora del servidor de bases de datos
    // Simplemente ejecuta sql_func( 'SELECT now() ' );
    function sql_now: string;

    // Solicita la ejecución de la orden xo al demonio ROS pasando la lista de parámetros
    function fros(xo: string; const paramNames, paramValues: array of string): string;

    function ros_simsee_mail(const email, asunto, texto: string): boolean;
    function ros_mail(const email, asunto, texto, replayto: string): boolean;

    // retona la ip y el puerto visible desde el exterior.
    function ros_getmyipandport(var ip: shortstring; var port: word): boolean;

    function now(): TDateTime;

    (*Utilidades para tablas indexadas por 'nid' *)

    // clona el record 'nidrec' de la tabla 'tabla' asignandole el nid 'nuevo_nid'
    // si 'nuevo_nid' es <= 0 entonces se solicita un nuevo nid al generador de sequencias
    // de la tabla. El resultado de la función es el nid del nuevo registro.
    // El parámetro set_on_the_fly permite especificar cambios a realizar en los parámetros
    // del registro durante la copia. Por ejemplo, si el record que se está clonando tiene
    // un campo 'activa' y se quiere asegurar que en nuevo registro tenga activa=0 desde el
    // comienzo, se puede especificar el parámetro set_on_the_fly como: 'activa=0'
    function clonar_rec(tabla: string; nidrec: integer; nuevo_nid: integer;
      set_on_the_fly: string): integer;

    // Busca en la tabla 'tabla_hijas' todas las filas con campo_link= nid_madre_origen y los
    // clona insertándolos en 'tabla_hijas' con nuevos nids generados automaticamente y a las copias
    // les imporne el valor nid_madre_destino en el campo 'campo_link'
    procedure clonar_hijas(tabla_hijas, campo_link: string;
      nid_madre_origen, nid_madre_destino: integer);

    // Clona las hijas y en forma recursiva clona las nietas llamnado clonar_hijas
    // para cada hija clonada.
    procedure clonar_hijas_y_nietas(
      tabla_hijas, tabla_nietas, campo_link_hijas, campo_link_nietas: string;
      nid_madre_origen, nid_madre_destino: integer);

    {$IFDEF SININTERFASES_FEDE}
    function Query(sql: string): TDataSetGenerico;
    {$ELSE}
    function Query(sql: string): IDataset;
    {$ENDIF}
    function Exec(sql: string): integer;

    function func(const sqlstr: string; defval: string = ''): string;
    function funcI(const sqlstr: string; defval: integer = -1): integer;
    function funcF(const sqlstr: string; defval: double = -1): double;
    function funcS(const sqlstr: string; defval: string = ''): string;
    function funcD(const sqlstr: string; defval: TDateTime = 0.0): TDateTime;

  private
    function ros(xo: string; ParamStr: string; max_reintentos: integer = 0;
    // reintanta si detecta error en comunicación. OJO, no es cualquier POST que tiene validez reintantar
      pass: boolean = False): TStringList; overload;

       {$IFDEF PROXY_ENABLED_ROS}
    function ProxyHttpPostURL(const URL, URLData: string; const Data: TStream
      ): boolean;
       {$ENDIF}
    function fros_str(xo: string; ParamStr: string): string;

  end;

  TDBCon = TDBrosxCon;

// calcula el checksum de un buffer de bytes y retorna un string de 16 caracteres
// con el resultado en formato hexadecimal
function checksum(pbuff: pointer; nBytes: cardinal): shortstring;

function escapeChars(const s: string): string; overload;


function URLEncode(Str: string): string;
function URLDecode(Str: string): string;


//funciones auxileares para armado de los SQL de Insert y Update
procedure AppendToInsertStr(var str_nombres, str_valores: string;
  const nombre, valor: string; encomille: boolean = True);
procedure AppendToUpdateStr(var str_update: string; const nombre, valor: string;
  encomille: boolean = True);


type
  ConsultaSQLException = class(Exception)
    constructor Create(const msg: string);
  end;

//Retorna un string formado por un número de 4 dígitos al azar
function gen_codigo: string;

// encripta el string s con un método propio. El resultado
// tiene el doble de largo que el original y es alfanumerio.
function barullar(s: string): string;

// dado un string barullado con barullar lo desenbarulla
function desbarullar(s: string): string;


function GetIpByName(hostName: string): string;
// retorna el nombre de la maquina local.
function GetLocalHostName: string;
function IP4StrToCardinal(const AIpAddress: shortstring): cardinal;

{$IFNDEF PROXY_ENABLED_ROS}

// abre un socket cliente -- por defecto del tipo SOCK_STREAM, se puede especificar SOCK_DGRAM para UDP
function rosx_cliOpen( var sock: TSocket; hostId: cardinal; port: integer; tipo: cint = SOCK_STREAM ): boolean;
function rosx_cliClose( var sock: TSocket): boolean;
{$ENDIF}

//  estas funciones suponen que Buff existe y tiene largo nbytes
function BuffToHexStr_(const buff; nbytes: integer): string;
function PG_BuffToHexStr(const buff; nbytes: integer): string;

procedure HexStrToBuff_(var buff; nbytes: integer; hexStr: string);
procedure PG_HexStrToBuff(var buff; nbytes: integer; hexStr: string);

// Codifica cada caracer como un Hex de dos dígitos
function BuffStrToHexStr_(const buffStr: string): string;

// Decodifica cada dos caracteres como el Hex  del caracter
function HexStrToBuffStr_(const hexStr: string): string;

function PG_BuffStrToHexStr(const BuffStr: string): string;
function PG_HexStrToBuffStr(const hexStr: string): string;


// si xstr = '' retorna xdefaul: sino rentora xstr
function if_vacio(xstr, xdefault: string): string;


var
  ultimo_error: string;

implementation


function if_vacio(xstr, xdefault: string): string;
begin
  if xstr = '' then
    Result := xdefault
  else
    Result := xstr;
end;

function digHexToInt(c: char): integer;
begin
  case c of
    '0'..'9': Result := Ord(c) - Ord('0');
    'A'..'F': Result := 10 + Ord(c) - Ord('A');
    'a'..'f': Result := 10 + Ord(c) - Ord('a');
    else
      raise Exception.Create('uros.digHexToInt: caracter inválido "' + c + '"');
  end;
end;


function HexToInt(s: string): integer;
var
  res, k: integer;
  //  c: char;
begin
  res := 0;
  for k := 1 to length(s) do
    res := res * 16 + digHexToInt(s[k]);
  Result := res;
end;


type
  TBuffer_de_bytes = packed array[0..1024 * 1024 * 100] of byte;



function BuffToHexStr_(const buff; nbytes: integer): string;
var
  res: string;
  k: integer;
  b: byte;
begin
  res := '';
  for k := 0 to nbytes - 1 do
  begin
    b := TBuffer_de_bytes(buff)[k];
    res := res + IntToHex(b, 2);
  end;
  Result := res;
end;

function PG_BuffToHexStr(const buff; nbytes: integer): string;
begin
  Result := '\x' + BuffToHexStr_(buff, nbytes);
end;

procedure HexStrToBuff_(var buff; nbytes: integer; hexStr: ansistring);
var
  k: integer;
  n: integer;
  b: byte;
  s2: string;
begin
  n := length(hexStr);
  if n <> nbytes * 2 then
    raise Exception.Create('HexStrToBuff .. nbytes * 2 <> length( hexStr ) : nbytes: ' +
      IntToStr(nbytes) + ', length(hexStr): ' + IntToStr(length(hexStr)) +
      ', hexstr: ' + hexStr);

  for k := 0 to nbytes - 1 do
  begin
    s2 := copy(hexStr, k * 2 + 1, 2);
    b := HexToInt(s2);
    TBuffer_de_bytes(buff)[k] := b;
  end;
end;

procedure PG_HexStrToBuff(var buff; nbytes: integer; hexStr: ansistring);
var
  s: string;
begin
  if (hexStr[1] = '\') and (hexStr[2] = 'x') then
    s := copy(hexStr, 3, length(hexStr) - 2)
  else
    s := hexStr;
  HexStrToBuff_(buff, nbytes, s);
end;

function BuffStrToHexStr_(const buffStr: string): string;
begin
  Result := BuffToHexStr_(buffStr[1], length(buffStr));
end;


function HexStrToBuffStr_(const hexStr: string): string;
var
  res: string;
begin
  setlength(res, length(hexStr) div 2);
  HexStrToBuff_(res[1], length(res), hexStr);
  Result := res;
end;

function PG_BuffStrToHexStr(const BuffStr: string): string;
var
  s: string;
begin
  s := '\x' + BuffStrToHexStr_(BuffStr);
  Result := s;
end;

function PG_HexStrToBuffStr(const hexStr: string): string;
var
  s: string;
begin

  if (hexStr[1] = '\') and (hexStr[2] = 'x') then
    s := copy(hexStr, 3, length(hexStr) - 2)
  else
    s := hexStr;
  s := HexStrToBuffStr_(s);
  Result := s;
  //  result:= base64.DecodeStringBase64( hexStr );
end;

function gen_codigo: string;
var
  res: string;
  i: integer;
begin
  res := '';
  randomize();
  for i := 0 to 3 do
    res := res + IntToStr(Random(9) + 1);
  Result := res;
end;

function barullar(s: string): string;
var
  rs: string;
  k: integer;
  b, c: byte;
  dr, dl: byte;
begin
  c := 33;
  dr := 31 mod 8;
  dl := 8 - dr;
  rs := '';
  for k := 1 to length(s) do
  begin
    b := Ord(s[k]);
    b := ((b shr dr) + (b shl dl)) mod 256;
    b := b xor c;
    c := b;
    rs := rs + IntToHex(b, 2);
  end;
  Result := rs;
end;

function desbarullar(s: string): string;
var
  rs: string;
  i: integer;
  c, cs, b: byte;
  n: integer;
  dr, dl: byte;
begin
  c := 33;
  dl := 31 mod 8;
  dr := 8 - dl;
  rs := '';
  //  i:= 0;
  n := length(s) div 2;
  for i := 1 to n do
  begin
    b := HexToInt(copy(s, (i - 1) * 2 + 1, 2));
    cs := b;
    b := b xor c;
    b := ((b shl dl) + (b shr dr)) mod 256;
    rs := rs + chr(b);
    c := cs;
  end;
  Result := rs;
end;

function Octal3dToChar(s: string): char;
begin
  Result := char(((Ord(s[3]) - 48) * 8 + (Ord(s[2]) - 48)) *
    8 + Ord(s[3]));
end;



function escapeChars(const s: string): string;
const

  escapables = [#0, '''', '"', #13, #10, #9, #26, '\'];

  //  ['\', #39, #34, #0, #10, #13, #26], ['\\','\'#39,'\'#34,'\0','\n','\r','\Z'] ,
var
  ir, iw: integer;
  cnt: integer;
  res: string;
begin
  cnt := 0;
  for ir := 1 to length(s) do
    if s[ir] in escapables then
      Inc(cnt);

  setlength(res, length(s) + cnt);
  iw := 1;
  for ir := 1 to length(s) do
  begin
    if s[ir] in escapables then
    begin
      res[iw] := '\';
      Inc(iw);
      case s[ir] of
        #0: res[iw] := '0';
        #13: res[iw] := 'r';
        #10: res[iw] := 'n';
        #26: res[iw] := 'Z';
        else
          res[iw] := s[ir];
      end;
    end
    else
      res[iw] := s[ir];
    Inc(iw);
  end;
  Result := res;
end;


function URLEncode(Str: string): string;
var
  i, j: integer;
  res, s2: string;
begin

  setlength(res, length(str) * 3);
  j := 1;
  for i := 1 to Length(Str) do
    if Str[i] in ['A'..'Z', 'a'..'z', '0'..'9', '-', '_', '.'] then
    begin
      Res[j] := Str[i];
      Inc(j);
    end
    else
    begin
      Res[j] := '%';
      Inc(j);
      s2 := IntToHex(Ord(Str[i]), 2);
      Res[j] := s2[1];
      Inc(j);
      Res[j] := s2[2];
      Inc(j);
    end;
  setlength(res, j - 1);
  Result := res;
end;

function URLDecode(Str: string): string;
var
  i: integer;
  res: string;
  j, k: integer;
  s2: string;
  c: char;
  n: integer;
begin
  //  result:= synacode.DecodeURL( Str );
  n := length(str);
  setlength(res, n);
  j := 1;
  k := 1;
  while k <= n do
  begin
    c := str[k];
    Inc(k);
    if (c = '%') then
    begin
      s2 := '$' + copy(str, k, 2);
      Inc(k, 2);
      if not TryStrToInt(s2, i) then
      begin
        Result := '';
        exit;
      end;
      res[j] := chr(i);
    end
    else
      res[j] := c;
    Inc(j);
  end;
  setlength(res, j - 1);
  Result := res;
end;

function checksum(pbuff: pointer; nBytes: cardinal): shortstring;
type
  TLARB = packed array[0..1024 * 1024 * 1024] of byte;
var
  res: int64;
  p: ^TLARB;
  k: cardinal;
  b: byte;
  //  ress: shortstring;
begin
  p := pbuff;
  res := 0;
  for k := 0 to nBytes - 1 do
  begin
    b := p^[k];
    res := ((res shr 1) + (res shl 63)) xor b;
  end;
  Result := inttohex(res, 16);
end;




//funciones auxileares para armado de los SQL de Insert y Update
procedure AppendToInsertStr(var str_nombres, str_valores: string;
  const nombre, valor: string; encomille: boolean = True);
begin
  if str_nombres <> '' then
  begin
    str_nombres := str_nombres + ', ';
    str_valores := str_valores + ', ';
  end;
  str_nombres := str_nombres + nombre;
  if encomille then
    str_valores := str_valores + '''' + valor + ''''
  else
    str_valores := str_valores + valor;
end;

procedure AppendToUpdateStr(var str_update: string; const nombre, valor: string;
  encomille: boolean = True);
begin
  if str_update <> '' then
    str_update := str_update + ', ';
  str_update := str_update + nombre + ' = ';
  if encomille then
    str_update := str_update + '''' + valor + ''''
  else
    str_update := str_update + valor;
end;

function GetIpByName(hostName: string): string;
var
{$IFDEF WINDOWS}
  HEnt: pHostEnt;
{$ELSE}
  Hent: THostEntry;
{$ENDIF}
  i: integer;
  IPaddr: string;
  Name: string;
  flgFrom_hosts: boolean;

begin
  IpAddr := '';

  {$IFDEF WINDOWS}
  Name := HostName + #0;
  HEnt := GetHostByName(@Name[1]);
  if HEnt = nil then
    raise Exception.Create('GetIpByName: error en GetHostByName(' +
      hostName + '). WSAGetLastError= ' + IntToStr(WSAGetLastError));
  for i := 0 to HEnt^.h_length - 1 do
    IPaddr :=
      Concat(IPaddr, IntToStr(Ord(HEnt^.h_addr_list^[i])) + '.');
  {$ELSE}
  flgFrom_hosts := True;
  if not GetHostByName(hostName, HEnt) then
  begin
    flgFrom_hosts := False;
    if not ResolveHostByName(hostName, HEnt) then
      raise Exception.Create('GetIpByName: error en GetHostByName(' + hostName + ').');
  end;

  if flgFrom_hosts then
  begin
    for i := 4 downto 1 do
      IPaddr :=
        Concat(IPaddr, IntToStr(Ord(HEnt.addr.s_bytes[i])) + '.');
  end
  else
  begin
    for i := 1 to 4 do
      IPaddr :=
        Concat(IPaddr, IntToStr(Ord(HEnt.addr.s_bytes[i])) + '.');
  end;
  {$ENDIF}
  SetLength(IPaddr, Length(IPaddr) - 1);
  Result := IpAddr;
end;

{$IFDEF WINDOWS}
function GetLocalHostName: string;
type
  Name = array[0..100] of char;
  PName = ^Name;
var
  HName: PName;
begin
  New(HName);
  if GetHostName(HName^, SizeOf(Name)) = 0 then
    Result := StrPas(HName^)
  else
    Result := '';
  Dispose(HName);
end;

{$ELSE}
function GetLocalHostName: string;
begin
  Result := unix.GetHostName;
end;

{$ENDIF}

function IP4StrToCardinal(const AIpAddress: shortstring): cardinal;
var
  Retvar, i: longword;
  sData, sSeg: string;
begin
  Retvar := 0;
  sData := trim(AIpAddress);
  while sData <> '' do
  begin
    Retvar := Retvar shl 8;
    i := pos('.', sData);
    if i <> 0 then
    begin
      sSeg := copy(sData, 1, i - 1);
      sData := copy(sData, i + 1, length(sData));
    end
    else
    begin
      sSeg := sData;
      sData := '';
    end;
    Retvar := Retvar + (longword(StrToIntDef(sSeg, 0)));
  end;
  Result := Retvar;
end;



{$IFNDEF PROXY_ENABLED_ROS}

function rosx_cliClose(var sock: TSocket): boolean;
var
  res: longint; // longint
begin
  res := closeSocket(sock);
  Result := res = 0;
end;

function rosx_cliOpen(var sock: TSocket; hostId: cardinal; port: integer; tipo: cint = SOCK_STREAM ): boolean;
var
  v, addr, y: sockaddr_in;
  k: integer;
begin
  addr.sin_family := PF_INET;
  addr.sin_port := htons(port); // ShortHostToNet(port);

  {$IFDEF LINUX}
  addr.sin_addr.S_addr := longword(HostTonet(longint(hostId)));
  // htonl( longInt( hostId )); //
  {$ELSE}
  addr.sin_addr.S_addr := htonl(u_long(hostId)); //HostTonet(longint(h.IPAddress));
  {$ENDIF}

  for k := 0 to 7 do
  {$IFDEF LINUX}
    addr.xpad[k] := #0;
  {$ELSE}
  addr.sin_zero[k] := chr(0);
  {$ENDIF}

{$IFDEF LINUX}
  sock := fpSocket(PF_INET, tipo, 0);
  if sock = -1 then
{$ELSE}
    sock := Socket(PF_INET, tipo, 0);
  if WSAGetLastError <> 0 then
{$ENDIF}
  begin
    ultimo_Error := 'cliOpen: Error creando socket.';
    Result := False;
    exit;
  end;

  {$IFDEF LINUX}
  if fpConnect(sock, @addr, sizeOf(addr)) <> 0 then
  begin
    ultimo_Error := 'cliOpen: Error conectando socket, socket= ' + IntToStr(sock);
    rosx_cliClose(sock);
    Result := False;
    Exit;
  end;
  {$ELSE}
  if Connect(sock, addr, sizeOf(addr)) <> 0 then
  begin
    ultimo_Error := 'cliOpen: Error conectando socket, WSAGetLastError= ' +
      IntToStr(WSAGetLastError) + ', socket= ' + IntToStr(sock);
    rosx_cliClose(sock);
    Result := False;
    Exit;
  end;
  {$ENDIF}
  Result := True;
end;

type
  TBufBytes = packed array[0..1024 * 100] of byte;

function cliSendAll(s: TSocket; var Buf; len: integer): boolean;
var
  total: integer;        // cuántos bytes hemos enviado
  bytesleft: integer; // cuántos se han quedado pendientes
  n: integer;
begin
  total := 0;
  bytesleft := len;

  while (total < len) do
  begin
{$IFDEF LINUX}
    n := fpsend(s, @TBufBytes(buf)[total], bytesleft, 0);
{$ELSE}
    n := send(s, TBufBytes(buf)[total], bytesleft, 0);
{$ENDIF}
    if (n = -1) then
      break;
    total := total + n;
    bytesleft := bytesleft - n;
  end;
  Result := len = total;
end;

function cliWrite(s: TSocket; const r: string): boolean;
var
  ts: string;
begin
  ts := r;
  Result := cliSendAll(s, ts[1], length(ts));
end;


function cliWriteln(s: TSocket; const r: string): boolean;
var
  ts: string;
begin
  ts := r + #10;
  Result := cliSendAll(s, ts[1], length(ts));
end;

function cliReadln_shortString(var sock: TSocket; var buf: ShortString;
  var rln: boolean): boolean;
var
  kw: integer;
  tam: integer;
  nleidos: integer;
  socketCerrado: boolean;
begin
  rln := False;
  tam := 255;
  kw := 1;
  buf[0] := #0;
  socketCerrado := False;
  //  nleidos:= 0;
  while ((tam > 0)
      //    and ( buf[kw-1] <> #13 )
      and (buf[kw - 1] <> #10)) and not socketCerrado do
  begin
{$IFDEF LINUX}
    nleidos := fprecv(sock, @buf[kw], 1, 0);
{$ELSE}
    nleidos := recv(sock, buf[kw], 1, 0);
{$ENDIF}
    if nleidos > 0 then
    begin
      kw := kw + nleidos;
      tam := tam - nleidos;
    end
    else
      socketCerrado := True;
  end;

  nleidos := kw - 1;

  while (nleidos > 0) and ((buf[nleidos] = #13) or (buf[nleidos] = #10)) do
  begin
    Dec(nleidos);
    rln := True;
  end;
  buf[0] := chr(nleidos);
  Result := not socketCerrado;
end;


function cliReadln(var sock: TSocket; var res: string): boolean;
var
  ress: ShortString;
  lecturaOk: boolean;
  rln: boolean;
begin
  res := '';
  repeat
    ress := '';
    lecturaOk := cliReadln_ShortString(sock, ress, rln);
    res := res + ress;
  until (rln or not LecturaOk);
  Result := lecturaOk;
end;

{$ENDIF}



{$IFDEF PROXY_ENABLED_ROS}


function TDBrosxCon.ProxyHttpPostURL(const URL, URLData: string;
  const Data: TStream): boolean;

var
  HTTP: THTTPSend;
  res: boolean;
begin
  HTTP := THTTPSend.Create;
  try
    HTTP.ProxyHost := self.proxy_host;
    HTTP.ProxyPort := self.proxy_port;
    HTTP.ProxyUser := self.proxy_user;
    HTTP.ProxyPass := self.proxy_pass;
    HTTP.Document.Write(Pointer(URLData)^, Length(URLData));


    HTTP.KeepAlive:= true;
    HTTP.KeepAliveTimeout:=10;
    HTTP.Timeout := 6 * 60 * 1000; // 6 minutos, DV@20180710

    HTTP.MimeType := 'application/x-www-form-urlencoded';
    HTTP.UserAgent:= 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/537.36';

    HTTP.UserName:= Usuario_http;
    HTTP.Password:= Clave_http;

(*
    if referrer <> '' then
      HTTP.Headers.Add('Referrer: '+referrer );
  *)
    Res := HTTP.HTTPMethod('POST', URL);
//    Res := HTTP.HTTPMethod('GET', URL);
    Data.CopyFrom(HTTP.Document, 0);
  finally
    HTTP.Free;
  end;
end;
    (**
var
  HTTP: THTTPSend;
begin
  HTTP := THTTPSend.Create;
  try
    HTTP.Timeout:= 60;
    HTTP.KeepAlive:= true;
    HTTP.KeepAliveTimeout:=10;
    HTTP.Timeout := 6 * 60 * 1000; // 6 minutos, DV@20180710

    HTTP.ProxyHost := proxy_host;
    HTTP.ProxyPort := proxy_port;
    HTTP.ProxyUser := proxy_user;
    HTTP.ProxyPass := proxy_pass;
    HTTP.Document.Write(Pointer(URLData)^, Length(URLData));
    HTTP.MimeType := 'application/x-www-form-urlencoded';
    HTTP.UserAgent:= 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/537.36';
    Result := HTTP.HTTPMethod('POST', URL);
    Data.CopyFrom(HTTP.Document, 0);
  finally
    HTTP.Free;
  end;
end;
       ***)
{$ENDIF}



// si nuevoNid <= 0 solicita uno sobre la secuencia con el mismo nombre de la
// tabla.
function TDBrosxCon.clonar_rec(tabla: string; nidrec: integer;
  nuevo_nid: integer; set_on_the_fly: string): integer;
var
  sql: string;
  nidnuevo: integer;
  tmptabla: string;
begin
  if nuevo_Nid <= 0 then
    nidnuevo := sql_nextnid(tabla)
  else
    nidnuevo := nuevo_nid;

  tmptabla := tabla + '_bk_tmp' + IntToStr(nidrec);

  sql_exec('DROP TABLE IF EXISTS ' + tmptabla);

  sql_exec('CREATE TABLE ' + tmptabla + ' AS ( SELECT * FROM ' +
    tabla + ' WHERE nid=' + IntToStr(nidrec) + ' ) ');

  sql := 'UPDATE ' + tmptabla + ' SET nid=' + IntToStr(nidnuevo);
  if set_on_the_fly <> '' then
    sql := sql + ', ' + set_on_the_fly;
  sql_exec(sql);

  sql_exec('INSERT INTO ' + tabla + ' SELECT * FROM ' + tmptabla);
  sql_exec('DROP TABLE ' + tmptabla);

  Result := nidnuevo;
end;


procedure TDBrosxCon.clonar_hijas(tabla_hijas, campo_link: string;
  nid_madre_origen, nid_madre_destino: integer);
var
  ds: TResultadoQuery;
  r: TDataRecord;
  nid_hija_origen: integer;
  setonfly: string;
begin
  setonfly := campo_link + ' = ' + IntToStr(nid_madre_destino);
  ds := sql_query('SELECT nid FROM ' + tabla_hijas + ' WHERE ' + campo_link +
    ' = ' + IntToStr(nid_madre_origen) + ' ORDER BY nid ');
  ds.First;
  r := ds.getFilaActiva;

  while r <> nil do
  begin
    nid_hija_origen := r.GetByNameAsInt('nid');
    clonar_rec(tabla_hijas, nid_hija_origen, -1, setonfly);
    ds.Next;
    r := ds.getFilaActiva;
  end;
  ds.Free;
end;



procedure TDBrosxCon.clonar_hijas_y_nietas(
  tabla_hijas, tabla_nietas, campo_link_hijas, campo_link_nietas: string;
  nid_madre_origen, nid_madre_destino: integer);
var
  ds: TResultadoQuery;
  r: TDataRecord;
  nid_hija_origen: integer;
  setonfly: string;
  nid_hija_destino: integer;

begin
  setonfly := campo_link_hijas + ' = ' + IntToStr(nid_madre_destino);
  ds := sql_query('SELECT nid FROM ' + tabla_hijas + ' WHERE ' +
    campo_link_hijas + ' = ' + IntToStr(nid_madre_origen) + ' ORDER BY nid ');

  ds.First;
  r := ds.getFilaActiva;

  while r <> nil do
  begin
    nid_hija_origen := r.GetByNameAsInt('nid');
    nid_hija_destino := clonar_rec(tabla_hijas, nid_hija_origen, -1, setonfly);
    clonar_hijas(tabla_nietas, campo_link_nietas, nid_hija_origen, nid_hija_destino);
    ds.Next;
    r := ds.getFilaActiva;
  end;
  ds.Free;
end;


function TDBrosxCon.sql_nextnid(nombre: string): integer;
begin
  Result := StrToInt(fros('next_nid', ['tabla'], [nombre]));
end;

function TDBrosxCon.sql_nextnid_pass(nombre, pass: string): integer;
begin
  Result := StrToInt(fros('nube_next_nid', ['tabla', 'pasaporte'], [nombre, pass]));
end;

function TDBrosxCon.sql_nextnidarchivo_pass(nombre, pass: string): integer;
begin
  Result := StrToInt(fros('nube_next_nidarchivo', ['tabla', 'pasaporte'],
    [nombre, pass]));
end;

function TDBrosxCon.nextval(nombre: string): integer;
begin
  Result := sql_nextnid(nombre);
end;

function TDBrosxCon.sql_exec(sql: string): boolean;
var
  queryRes: TResultadoQuery;
begin
  try
    queryRes := TResultadoQuery.CreateExec(self, sql);
    queryRes.Free;
    Result := True;
  except
    on E: Exception do
    begin
      ultimoError := E.Message;
      Result := False;
    end;
  end;
end;

function TDBrosxCon.sql_query(sql: string; maxNReintentos: integer): TResultadoQuery;
var
  queryRes: TResultadoQuery;
begin
  try
    queryRes := TResultadoQuery.CreateQuery(self, sql, maxNReintentos);
    Result := queryRes;
  except
    on E: Exception do
    begin
      ultimoError := E.Message;
      Result := nil;
    end;
  end;
end;

function TDBrosxCon.sql_query_pass(pass: string; nid, nid_version: integer;
  Data: string): TResultadoQuery;
var
  queryRes: TResultadoQuery;
  nid_version_str: String;
begin
  try
    if nid_version=0 then
      nid_version_str:=''
    else
      nid_version_str:=IntToStr(nid_version);
    queryRes := TResultadoQuery.CreateNubeQuery_pass(
      self, pass, IntToStr(nid), nid_version_str, Data);
    Result := queryRes;
    ultimoError := queryRes.error;
  except
    on E: Exception do
    begin
      ultimoError := E.Message;
      Result := nil;
    end;
  end;

end;

function TDBrosxCon.sql_exec_pass(pass, Data: string;
  nid_version, nid_data, nBloque: integer; permiso: string): integer;
var
  queryRes: TResultadoQuery;
begin
  try
    queryRes := TResultadoQuery.CreateNubeExec_pass(self,
      pass, IntToStr(nid_version), IntToStr(nid_data), IntToStr(nBloque),
      Data, permiso);
    Result := queryRes.nid;
    ultimoError := queryRes.error;
    queryRes.Free;
  except
    on E: Exception do
    begin
      ultimoError := E.Message;
      Result := -1;
    end;
  end;

end;

function TDBrosxCon.sql_login(usr, clv: string): string;
var
  queryRes: TResultadoQuery;
begin
  try
    queryRes := TResultadoQuery.CreateLogin(self, usr, clv);
    Result := queryRes.pasaporte;
  except
    on E: Exception do
    begin
      ultimoError := E.Message;
      Result := '';
    end;
  end;
end;

function TDBrosxCon.sql_logout(pass: string): boolean;
var
  queryRes: TResultadoQuery;
begin
  try
    queryRes := TResultadoQuery.CreateLogout(self, pass);
    queryRes.Free;
    Result := True;
  except
    on E: Exception do
    begin
      ultimoError := E.Message;
      Result := False;
    end;
  end;

end;


{$IFDEF SININTERFASES_FEDE}
function TDBrosxCon.Query(sql: string): TDataSetGenerico;
{$ELSE}
function TDBrosxCon.Query(sql: string): IDataset;
{$ENDIF}
begin
  Result := sql_query(sql);
end;


function TDBrosxCon.Exec(sql: string): integer;
begin
  if sql_exec(sql) then
    Result := 0
  else
    Result := -1;
end;

// hace la consulta y retorna un string con el resultado
// si es error retorna '' El string vacío.
// Esta llamada es últil para funciones que retornan un sólo valor
function TDBrosxCon.sql_func(sql: string; maxNReintentos: integer): string;
var
  ds: TResultadoQuery;
  r: TDataRecord;
begin
  ds := sql_query(sql, maxNReintentos);
  if (ds <> nil) and (ds.nrows > 0) then
  begin
    ds.first;
    r := ds.getFilaActiva;
    Result := r.GetByIdAsString(0);
    ds.Free;
  end
  else
    Result := '';
end;




// hace la consulta y retorna el record (FICHA) correspondiente
// si da error retorna nil. El usuario debe llamar al FREE de la ficha
// Es útil para las consultas en las que se quiere trabajar sobre un único record
function TDBrosxCon.sql_ficha(sql: string): TDataRecord;
var
  ds: TResultadoQuery;
  r: TDataRecord;
begin
  ds := sql_query(sql);
  if (ds <> nil) and (ds.nrows > 0) then
  begin
    ds.first;
    r := ds.getFilaActiva;
    r.freePadre := True; // le indicamos a la ficha que en su FREE libere los resultados
    Result := r;
  end
  else
    Result := nil;
end;




function TDBrosxCon.func(const sqlstr: string; defval: string): string;
var
  s: string;
begin
  s := self.sql_func(sqlstr);
  if s <> '' then
    Result := s
  else
    Result := defval;
end;

function TDBrosxCon.funcI(const sqlstr: string; defval: integer): integer;
var
  s: string;
begin
  s := func(sqlstr, IntToStr(defval));
  Result := StrToInt(s);
end;

function TDBrosxCon.funcF(const sqlstr: string; defval: double): double;
var
  s: string;
begin
  s := func(sqlstr, FloatToStr(defval));
  Result := StrToFloat(s);
end;

function TDBrosxCon.funcS(const sqlstr: string; defval: string): string;
begin
  Result := func(sqlstr, defval);
end;

function TDBrosxCon.funcD(const sqlstr: string; defval: TDateTime): TDateTime;
var
  s: string;
  af: TFormatSettings;
begin
  s := func(sqlstr, FormatDateTime('yyyy-mm-dd hh:nn:ss', defval));

  af := SysUtils.FormatSettings;
  af.DateSeparator := '-';
  af.ShortDateFormat := 'YYYY-MM-DD';
  af.ShortTimeFormat := 'hh:nn:ss';

  Result := StrToDateTime(s, af);
end;




function TDBrosxCon.sql_now: string;
begin
  Result := sql_func('SELECT now() ');
end;



{$IFDEF PROXY_ENABLED_ROS}

function TDBrosxCon.ros(xo: string; ParamStr: string; max_reintentos: integer;
  pass: boolean): TStringList;
label
  lbl_reintentar;
var
  url, error: string;
  uri, rb: string;
  buff: string;
  lineaInicioRes, lineaFinRes: string;
  res: TStringList;
  k: integer;
  st: TMemoryStream;
  buscando: boolean;
  cnt_reintentos: integer;


  function getpal: string;
  begin
    if res.Count > 0 then
    begin
      Result := res[0];
      res.Delete(0);
    end
    else
      Result := '';
    //   writeln( '['+result+']');
  end;

begin
  if not usuario_loginok then
    raise Exception.Create('Fallo de autenticación!!');
  if ipfija = '' then
  begin
    if pos('http', host) = 0 then
      url := 'http://' + host
    else
      url := host;
  end
  else
  begin
    if pos('http', host) = 0 then
      url := 'http://' + ipFija
    else
      url := ipFija;
  end;
  if puerto = '' then
    if pos('https:', host) = 1 then
      puerto := '443'
    else
      puerto := '80';

  url := url + ':' + puerto;

  uri := URI_ROSX;
  url := url + uri;
  if ParamStr <> '' then
    rb := 'xo=' + xo + '&' + ParamStr
  else
    rb := 'xo=' + xo;


  lineaInicioRes := '+inicio_' + xo;
  lineaFinRes := '+fin_' + xo;

  cnt_reintentos := 0;

  lbl_reintentar:

    res := nil;

  st := TMemoryStream.Create;
  try
    ProxyHTTPpostURL(url, rb, st);

 //   st.SaveToFile( 'strema.txt' );

    st.Seek(0, soFromBeginning);
    res := TStringList.Create;
    res.LoadFromStream(st);
  finally
    st.Free;
  end;

  if res = nil then
  begin
    if cnt_reintentos < 5 then
    begin
      Inc(cnt_reintentos);
      writeln('reintentando ... ');
      goto lbl_reintentar;
    end
    else
      raise Exception.Create('No es posible obtener la respuesta TDBrosxCon.ros');
  end;

  cnt_reintentos := 0;

//  res.SaveToFile( 'urosx.xlt' );

  buff := getpal;
  //  writeln( 'buff: <',buff, '>' );
  //  writeln( 'lineaInicioRes: <', lineaInicioRes, '>' );

  buscando := (buff <> lineaInicioRes);
  while (res.Count > 0) and buscando do
  begin
    if (buff = lineaInicioRes) then
      buscando := False
    else
      buff := getpal;
  end;

  if buscando then
  begin
    res.Free;

    if cnt_reintentos < max_reintentos then
    begin
      Inc(cnt_reintentos);
      writeln('reintentando: ', cnt_reintentos);
      goto lbl_reintentar;
    end;

    raise ConsultaSQLException.Create('TResultadoQuery.ros: error, no se recibió ' +
      lineaInicioRes);
  end;

  buff := getpal;
  if buff = '+error' then
  begin
    error := '';
    while (res.Count > 0) and (buff <> lineaFinRes) do
    begin
      buff := getpal;
      error := error + buff + #13#10;
    end;

    if pass then
    begin    //si entró con pasaporte, quiero ver el error
      ultimoError := error;
      Result := res;
      exit;
    end
    else
    begin
      res.Free;
      raise ConsultaSQLException.Create('TResultadoQuery.ros: ' + error);
    end;
  end;

  buscando := buff <> lineaFinRes;
  k := 0;
  while (buscando) and (k < res.Count) do
  begin
    if res[k] = lineaFinRes then
      buscando := False
    else
      Inc(k);
  end;
  assert(k < res.Count, 'uros.ros: error no se recibió ' + lineaFinRes +
    ' donde se esperaba.');
  // borramos el final
  while res.Count > k do
    res.Delete(k);
  Result := res;
end;


{$ELSE}

function TDbrosxCon.ros(xo: string; ParamStr: string; max_reintentos: integer = 0;
  // reintanta si detecta error en comunicación. OJO, no es cualquier POST que tiene validez reintantar
  pass: boolean = False): TStringList;
var
  ip: cardinal;
  s: TSocket;
  rh, uri, rb: string;
  Content_Length: cardinal;
  buff: string;
  ipStr: string;
  lineaInicioRes, lineaFinRes, error: string;
  res: TStringList;
begin

  if ipfija = '' then
  begin
    ipStr := GetIPByName(host);
  end
  else
    ipStr := ipfija;

  ip := IP4StrToCardinal(IpStr);
  if not rosx_cliopen(s, ip, StrToInt( puerto )) then
    raise Exception.Create('No pude abrir el socket. El servidor: ' +
      ipStr + ' en el puerto: ' + puerto +
      ' no contesto la solicitud. Ultimo Error: ' + UltimoError);

  uri := URI_ROSX;
  if ParamStr <> '' then
    rb := 'xo=' + xo + '&' + ParamStr
  else
    rb := 'xo=' + xo;
  Content_Length := length(rb);
  rh := 'POST ' + uri + ' HTTP/1.0' + #10 + 'Host: ' + host + #10 +
    'User-Agent: PostIt' + #10 + 'Content-Type: application/x-www-form-urlencoded' +
    #10 + 'Content-Length: ' + IntToStr(Content_Length) + #10 + #10 + rb + #10;
  cliWrite(s, rh);

  lineaInicioRes := '+inicio_' + xo;
  lineaFinRes := '+fin_' + xo;

  while (cliReadln(s, buff)) and (buff <> lineaInicioRes) do
  begin
    // writeln( buff );
    //Ignoro lo leído
  end;

  if buff <> lineaInicioRes then
  begin
    rosx_cliClose(s);
    raise ConsultaSQLException.Create('TResultadoQuery.ros: error, no se recibió ' +
      lineaInicioRes);
  end;

  cliReadln(s, buff);
  if buff = '+error' then
  begin
    error := '';
    while (cliReadln(s, buff)) and (buff <> lineaFinRes) do
      error := error + buff + #13#10;
    rosx_cliClose(s);
    raise ConsultaSQLException.Create('TResultadoQuery.ros: ' + error);
  end;

  res := TStringList.Create;
  while cliReadln(s, buff) and (buff <> lineaFinRes) do
  begin
    res.Add(buff);
  end;
  rosx_cliClose(s);
  assert(buff = lineaFinRes, 'uros.ros: error no se recibió ' +
    lineaFinRes + ' donde se esperaba.');
  Result := res;
end;

{$ENDIF}

function TDBrosxCon.fros_str(xo: string; ParamStr: string): string;
var
  rosRes: TStringList;
  res: string;
begin
  rosRes := ros(xo, ParamStr);
  //  assert((rosRes.Count = 1), 'uros.fros: se llamo a fros pero la orden devolvió mas de un resultado');
  res := rosRes[0];
  rosRes.Free;
  Result := res;
end;

function TDBrosxCon.fros(xo: string;
  const paramNames, paramValues: array of string): string;
var
  paramString: string;
  i: integer;
begin
  assert(Length(paramNames) = Length(paramValues));
  if Length(paramNames) > 0 then
  begin
    paramString := paramNames[0] + '=' + urlencode(paramValues[0]);
    for i := 1 to high(paramNames) do
      paramString := paramString + '&' + paramNames[i] + '=' + urlencode(paramValues[i]);
  end
  else
    paramString := '';
  Result := fros_str(xo, paramString);
end;

function TDBrosxCon.ros_getmyipandport(var ip: shortstring; var port: word): boolean;
var
  rosRes: TStringList;
  res: boolean;

begin
  res := True;
  try
    rosRes := ros('getmyipandport', '');
    ip := rosRes[0];
    port := StrToInt(rosRes[1]);
    rosRes.Free;
  except
    on e: ConsultaSQLException do
    begin
      ip := '';
      port := 0;
      res := False;
      ultimoError := e.Message;
    end;
  end;
  Result := res;
end;

function TDBrosxCon.now(): TDateTime;
var
  f: TField;
  ds: TResultadoQuery;
begin
  ds := sql_query('SELECT NOW();');
  f := ds.fields[0];
  Result := f.AsDateTime();
  f.Free();
  ds.Free();
end;


function TDBrosxCon.ros_mail(const email, asunto, texto, replayto: string): boolean;
var
  params: string;
  rosRes: TStringList;
  res: boolean;

begin
  res := True;

  params := 'email=' + URLEncode(email) + '&asunto=' + URLEncode(asunto) +
    '&texto=' + URLEncode(texto) + '&replayto=' + URLEncode(replayto);
  try
    rosRes := ros('mail', params);
    rosRes.Free;
  except
    on e: ConsultaSQLException do
    begin
      res := False;
      ultimoError := e.Message;
    end;
  end;
  Result := res;
end;


function TDBrosxCon.ros_simsee_mail(const email, asunto, texto: string): boolean;
var
  params: string;
  rosRes: TStringList;
  res: boolean;
  cuerpo: string;

begin
  res := True;

  cuerpo := texto + #13#10 + '-------------------------' + #13#10 +
    'Atentamente, el equipo de SimSEE.';

  params := 'email=' + URLEncode(email) + '&asunto=' + URLEncode(asunto) +
    '&texto=' + URLEncode(cuerpo);
  try
    rosRes := ros('mail', params);
    rosRes.Free;
  except
    on e: ConsultaSQLException do
    begin
      res := False;
      ultimoError := e.Message;
    end;
  end;
  Result := res;
end;


constructor TResultadoQuery.CreateQuery(dbcon: TDBrosxCon; sql: string;
  maxNReintentos: integer);
begin
  call_ros_constructor_helper(dbcon, 'query', sql, maxNReintentos);
  //  if self.nrows >0 then next;
end;

constructor TResultadoQuery.CreateExec(dbcon: TDBrosxCon; sql: string);
begin
  call_ros_constructor_helper(dbcon, 'exec', sql);
end;

procedure TResultadoQuery.call_ros_constructor_helper(dbcon: TDBrosxCon;
  orden: string; ParamStr: string; maxNReintentos: integer);
var
  lst_res: TStringList;
  i, j: integer;
  nfilas, ncampos: integer;
  k: integer;

begin
  lst_res := dbcon.ros(orden, 'sql=' + URLEncode(ParamStr), maxNReintentos);

  if lst_res.Count = 0 then
  begin
    inherited Create(0, 0);
  end
  else
  begin
    k := 0;

    nfilas := StrToInt(lst_res[k]);
    Inc(k);
    ncampos := StrToInt(lst_res[k]);
    Inc(k);

    inherited Create(nfilas, ncampos);

    for j := 0 to nfields - 1 do
    begin
      descripcionDeCampos[j].tipo := lst_res[k];
      Inc(k);
    end;
    for j := 0 to nfields - 1 do
    begin
      descripcionDeCampos[j].nombre := lowerCase(lst_res[k]);
      Inc(k);
    end;

    for i := 0 to nrows - 1 do
    begin
      //      resultados[i] := TDataRecord.Create(self);
      for j := 0 to nfields - 1 do
      begin
        filas_[i].SetValById(j, URLDecode(lst_res[k]));
        Inc(k);
      end;
    end;
  end;
  lst_res.Free;
end;


constructor TResultadoQuery.CreateLogin(dbcon: TDBrosxCon; usr, clv: string);
begin
  call_ros_constructor_helper_login(dbcon, 'login', usr, clv);
end;

constructor TResultadoQuery.CreateLogout(dbcon: TDBrosxCon; pass: string);
begin
  call_ros_constructor_helper_logout(dbcon, 'logout', pass);
end;

constructor TResultadoQuery.CreateNubeQuery_pass(dbcon: TDBrosxCon;
  pass, nid, nid_version, Data: string);
begin
  call_ros_constructor_helper_pass(dbcon, 'nube_query', pass, nid,
    nid_version, '', Data, '');
end;

constructor TResultadoQuery.CreateNubeExec_pass(dbcon: TDBrosxCon;
  pass, nid_version, nid_data, nBloque, Data, permiso: string);
begin
  call_ros_constructor_helper_pass(dbcon, 'nube_exec', pass, nid_data, nid_version,
    nBloque, Data, permiso);
end;

procedure TResultadoQuery.call_ros_constructor_helper_pass(dbcon: TDBrosxCon;
  orden: string; pass, nid, nid_version, nBloque, Data, permiso: string);
var
  lst_res: TStringList;
  i, j: integer;
  nfilas, ncampos: integer;
  k: integer;
  ParamStr: string;
begin

  ParamStr := 'pass=' + URLEncode(pass) + '&' + 'nid=' + URLEncode(nid) +
    '&' + 'nid_v=' + URLEncode(nid_version) + '&' + 'data=' +
    URLEncode(Data) + '& ' + 'nbloques=' + URLEncode(nBloque) +
    '& ' + 'permisos=' + URLEncode(permiso);

  lst_res := dbcon.ros(orden, ParamStr, 0, True);

  if lst_res.Count = 0 then
  begin
    inherited Create(0, 0);
    self.error := dbcon.ultimoError;
    self.nid := -2;
  end
  else
  begin
    k := 0;

    self.nid := StrToInt(lst_res[k]);
    Inc(k);
    nfilas := StrToInt(lst_res[k]);
    Inc(k);
    ncampos := StrToInt(lst_res[k]);
    Inc(k);
    inherited Create(nfilas, ncampos);

    for j := 0 to nfields - 1 do
    begin
      descripcionDeCampos[j].tipo := lst_res[k];
      Inc(k);
    end;
    for j := 0 to nfields - 1 do
    begin
      descripcionDeCampos[j].nombre := lowerCase(lst_res[k]);
      Inc(k);
    end;

    for i := 0 to nrows - 1 do
    begin
      //      resultados[i] := TDataRecord.Create(self);
      for j := 0 to nfields - 1 do
      begin
        filas_[i].SetValById(j, URLDecode(lst_res[k]));
        Inc(k);
      end;
    end;
  end;
  lst_res.Free;

end;

procedure TResultadoQuery.call_ros_constructor_helper_login(dbcon: TDBrosxCon;
  orden: string; usr, clv: string);
var
  lst_res: TStringList;
  ParamStr: string;
begin
  setSeparadoresGlobales;

  ParamStr := 'usr=' + URLEncode(usr) + '&' + 'clv=' + URLEncode(clv);

  lst_res := dbcon.ros(orden, ParamStr, 0, True);

  if lst_res.Count = 0 then
  begin
    pasaporte := '';
  end
  else
  begin
    pasaporte := lst_res[0];
  end;

  lst_res.Free;

end;

procedure TResultadoQuery.call_ros_constructor_helper_logout(dbcon: TDBrosxCon;
  orden: string; pass: string);
var
  lst_res: TStringList;
  i, j: integer;
  nfilas, ncampos: integer;
  k: integer;
  ParamStr: string;
begin

  ParamStr := 'pass=' + URLEncode(pass);

  lst_res := dbcon.ros(orden, ParamStr, 0, True);

  if lst_res.Count = 0 then
  begin
    inherited Create(0, 0);
  end
  else
  begin
    k := 0;

    nfilas := StrToInt(lst_res[k]);
    Inc(k);
    ncampos := StrToInt(lst_res[k]);
    Inc(k);
    inherited Create(nfilas, ncampos);

    for j := 0 to nfields - 1 do
    begin
      descripcionDeCampos[j].tipo := lst_res[k];
      Inc(k);
    end;
    for j := 0 to nfields - 1 do
    begin
      descripcionDeCampos[j].nombre := lowerCase(lst_res[k]);
      Inc(k);
    end;

    for i := 0 to nrows - 1 do
    begin
      //      resultados[i] := TDataRecord.Create(self);
      for j := 0 to nfields - 1 do
      begin
        filas_[i].SetValById(j, URLDecode(lst_res[k]));
        Inc(k);
      end;
    end;
  end;
  lst_res.Free;

end;



constructor ConsultaSQLException.Create(const msg: string);
begin
  inherited Create(msg);
end;

//=======================
// Métodos de TDBrosxCon
//+++++++++++++++++++++++

constructor TDBrosxCon.Create(ipfija: string; host: string; puerto: string;
  uri_rosx: string; usuario_http: string; clave_http: string);
begin
  inherited Create;
  usuario_loginok := True;
  usuario_tipo := 0;
  usuario_nid := 0;
  usuario_EsAdmin := False;
  self.ipfija := ipfija;
  self.host := host;
  self.puerto := puerto;
  self.uri_rosx := uri_rosx;
  // Por defecto ponemos MySQL para que anden las aplicaciones
  // desarrolladas inicialmente sobre MySQL.
  tipoServidor := CTS_MySQL;
  {$IFDEF PROXY_ENABLED_ROS}
  proxy_host := '';
  proxy_port := '';
  proxy_user := '';
  proxy_pass := '';
  {$ENDIF}
  self.Usuario_http:= Usuario_http;
  self.Clave_http:= Clave_http;
end;



procedure TDBrosxCon.determinar_tipo_servidor;
var
  s: string;
begin
  s := self.func('SELECT version(), ''ofe_''', '');
  if s = '' then
    raise Exception.Create('Imposible determinar el tipo de servidor');
  s := lowercase(s);
  if pos('postgresql', s) > 0 then
    tipoServidor := CTS_PostgreSQL
  else
    tipoServidor := CTS_MySQL;
end;


{$IFNDEF PROXY_ENABLED_ROS}
{$IFDEF WINDOWS}
procedure inicializar_WinSocket;
var
  wsadata: twsadata;
begin
  WSAStartUp(2 * 16 + 2, wsadata);
end;

procedure finalizar_WinSocket;
begin
  WSACleanUp;
end;


{$ENDIF}
{$ENDIF}


initialization
{$IFNDEF PROXY_ENABLED_ROS}
{$IFDEF WINDOWS}
  inicializar_WinSocket;
{$ENDIF}
{$ENDIF}

finalization

{$IFNDEF PROXY_ENABLED_ROS}
{$IFDEF WINDOWS}
  finalizar_WinSocket;
{$ENDIF}
{$ENDIF}
end.
