miércoles, 10 de julio de 2013

Codificación de base 2 a base 63 en JavaScript

El método toString sólo admite hasta la base 36, así que hay que partir de una nueva codificación. Base32 no tiene la especificación rfc4648, pero con el presente objeto se pueden codificar todos los caracteres desde la base 2 a la 63 (aunque podrían ser más):
//------------------------------------------------------------------------------------------------
// Engines      JavaScript 1.3+
//              JScript    5.5+
// Environments NN4.06+ IE5.5+ MOZILLA1+ SAFARI1+
//------------------------------------------------------------------------------------------------
var Coding =
{
      Num:
      {
            codec : '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
          , getBlockSize : function (b /* radix [2..63] */)
            {
                if      (b == 2)          b = 8;
                else if (b == 3)          b = 6;
                else if (b > 3 && b < 7)  b = 4;
                else if (b > 6 && b < 16) b = 3;
                else                      b = 2;
 
                return b;  
            }

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

          , encode: function (n /* number */, b /* radix [2..63] */)
            {
                var s = '';
                if (!isNaN(n) && !isNaN(b)) 
                {
                    if (typeof n == 'string')
                        n = parseInt(n);
 
                    b = Math.min(Math.max(2, b), 63);
       
                    do
                    {
                        s = this.codec.charAt(n % b) + s;
                        n = Math.floor(n / b)
                    }
 
                    while (n > 0);
     
                    n = Coding.Num.getBlockSize(b);
                    b = s.length;
     
                    while (b++ < n)
                        s = '0' + s;
                }

                return s;
            }

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

          , decode: function (s /* src */, b /* radix [2..63] */)
            {
                var n = 0;
                if (s != null && !isNaN(b))
                {
                    b = Math.min(Math.max(2, b), 63);
        
                    for (var i = 0, l = (s += '').length; i < l; ++i)
                        n = n * b + this.codec.indexOf(s.charAt(i));
                }

                return n;
            }
      }

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

    , Base2x63:
      {
            encode: function (s /* src */, b /* base [2..63] */)
            {
                var r = '';
                if (s != null && !isNaN(b))
                {
                    b = Math.min(Math.max(b, 2), 63);

                    for (var i = 0, l = (s += '').length; i < l; ++i)
                        r += Coding.Num.encode(s.charCodeAt(i), b);
                }

                return r;
            }

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

         , decode: function (s /* src */, b /* base [2..63] */)
           {
                var n, r = '';
                if (s != null && !isNaN(b))
                {
                    b = Math.min(Math.max(b, 2), 63);
                    n = Coding.Num.getBlockSize(b);
     
                    for (var i = 0, l = (s += '').length; i < l; i += n)
                        r += String.fromCharCode(Coding.Num.decode(s.substr(i, n), b));
                }

                return r;
           }
      }
};
Test:
var e, s = 'Tomaré una decisión con la cigüeña.';
for (var i = 2; i < 64; ++i)
{
    e = Coding.Base2x63.encode(s, i);
    alert
    (
        'base: ' + i + ', length: ' + e.length + ', encoded: ' + e + ', decoded: ' + 
        Coding.Base2x63.decode(e, i)
    );
}

jueves, 4 de julio de 2013

Test Criptográfico: PHP mcrypt y CryptoJS (AES)

<script type="text/javascript" src="cryptojs/3.1.2/rollups/aes.js"></script>
<script type="text/javascript" src="cryptojs/3.1.2/components/pad-zeropadding.js"></script>
<form method="post" enctype="application/x-www-form-urlencoded" action="crypto.php">
    <input type="text" id="md5" name="md5" size="100" maxlength="100" />
    <input type="text" id="code" name="code" size="100" maxlength="100" />
    <input type="submit" value="Test" />
</form>
var hash = CryptoJS.MD5('8765432187654321');
var key  = CryptoJS.enc.Utf8.parse(hash);
var iv   = CryptoJS.enc.Utf8.parse('1234567812345678');

document.getElementById('md5').value  = hash;
document.getElementById('code').value = CryptoJS.AES.encrypt
(
      'Test Ok!'
    , key
    , {iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.ZeroPadding }
);
<?php

$text      = "Test Ok!";
$key       = md5("8765432187654321");
$iv        = utf8_encode("1234567812345678");
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);
//$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($_POST["code"]), MCRYPT_MODE_CBC, $iv);

echo "md5: " . $_POST["md5"] . " -> " . $key . ", encrypted: " . $_POST["code"] . " -> " . base64_encode($encrypted) . ", decrypted: " . $decrypted; 

?>

miércoles, 3 de julio de 2013

Subir archivos con Ajax

//------------------------------------------------------------------------------------------------
// Engines      JavaScript 1.8.5+
//              JScript    10+
// Environments IE10+ FIREFOX4+ CHROME6+ OPERA12+ SAFARI5+
//------------------------------------------------------------------------------------------------
function upload 
(
      s // action-url
    , e // input file (id | element)
    , u // user
    , p // password
){
    if (typeof e != 'object')
        e = document.getElementById(e);
 
    var f, l = 0;

    if ((f = e.files) && (l = f.length) == 1) // HTML5! (FileList)
    {
        var h = new XMLHttpRequest();
  
        h.open('POST', s, false, u, p);
        h.setRequestHeader('Content-type', 'multipart/form-data');
        h.setRequestHeader('X-File-Name', (f = f[0]).name);
        h.setRequestHeader('X-File-Size', f.size);
        h.setRequestHeader('X-File-Type', f.type);
        h.send(f);
  
        return h.readyState == 4 && h.status == 200;
    }
 
    else if (l > 1)
    {
        f = [];
        for (var i = 0; i < l; ++i) f.push
        ({
               fileName : e.files[i].name
             , uploaded : upload(s, {files: [e.files[i]]}, u, p)
        });
  
        return f;
    }
 
    return false;
}

Uso con un sólo archivo:

<input id="file" type="file" />
if (upload('upload.php', 'file'))
{
    //TODO...
}

Uso con múltiples archivos:

<input id="files" type="file" multiple />
var r = upload('upload.php', 'files');

for (var i = 0, l = r.length; i < l; ++i) if (!r[i].uploaded) 
{
    alert('"' + r[i].fileName + '" Not uploaded correctly!');
    // TODO...
}


upload.php:
<?php

$hd   = getallheaders();
$name = $hd["X-File-Name"];

if (strlen($name) > 0)
{
    //$size = $hd["X-File-Size"];
    //$type = $hd["X-File-Type"];
    // TODO...

    $in  = fopen("php://input", "r");
    $out = fopen("upload/" . $name, "w");
    while($data = fread($in, 1024)) fwrite($out, $data);
    fclose($out);
    fclose($in);
}

?>
upload.jsp:
<%@ page import="java.io.*" %>
<%
    String fileName = request.getHeader("X-File-Name");
    if (fileName != null && fileName.length() > 0)
    {
        //String fileSize = request.getHeader("X-File-Size"); 
        //String fileType = request.getHeader("X-File-Type");    
        //TODO..
    
        FileOutputStream fos    = new FileOutputStream("webapps/test/upload/" + fileName);
        InputStream      is     = request.getInputStream();
        byte[]           buffer = new byte[1024];
        int              length = 0;
    
        while ((length = is.read(buffer)) != -1)
            fos.write(buffer, 0, length);
  
        fos.flush();
        fos.close();
    }
%>
upload.aspx:
<%@ Page Language="C#" %>
<%
if (Request.ContentLength > 0)
{
    string fileName = Request.Headers["X-File-Name"];

    if (fileName != null)
    {
        //string fileSize = Request.Headers["X-File-Size"];
        //string fileType = Request.Headers["X-File-Type"];
        //TODO...
         
        Request.SaveAs(Server.MapPath("/upload/") + fileName, false);
    }
}
%>
upload.asp:
<%@ Language="VBScript" %>
<%
Function Save (fileName, byteArray)
    on error resume next
    Dim  BinaryStream

    Set BinaryStream = CreateObject("ADODB.Stream")
   
    BinaryStream.Type = 1

    BinaryStream.Open
    BinaryStream.Write byteArray

    BinaryStream.SaveToFile Server.MapPath("upload") & "\" & fileName, 2
End Function

Dim strFileName
strFileName = Request.ServerVariables("HTTP_X-File-Name")

If (Len(strfileName) > 0) Then

    '... Request.ServerVariables("HTTP_X-File-Size")
    '... Request.ServerVariables("HTTP_X-File-Type")
    'TODO...

    Save strFileName, Request.BinaryRead(Request.TotalBytes)
End If
%>

martes, 2 de julio de 2013

Soporte "call" y "apply" para navegadores antiguos

Call:

//------------------------------------------------------------------------------------------------
// Engines      JavaScript 1.1+
//              JScript    2+
// Environments NN3+ IE4+ MOZILLA1+ SAFARI1+
//------------------------------------------------------------------------------------------------
function __fn_call (o /* object, arg1 [, ... [, argN]] */)
{
    var a = arguments;
    var s = '';
    var u;

    for (var i = 1, l = a.length, n = l - 1; i < l; ++i)
    {
        s += 'a[' + i + ']';
  
        if (i < n) 
            s+= ',';
    }
 
    if (o == null)
        s = eval('this(' + s + ')');
  
    else
    {
        o.__callback = this;
        s = eval('o.__callback(' + s + ')');
        o.__callback = u;
    }
 
    return s;
}

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

if (!Function.prototype.call) Function.prototype.call = __fn_call;

Apply:

//------------------------------------------------------------------------------------------------
// Engines      JavaScript 1.1+
//              JScript    2+
// Environments NN3+ IE4+ MOZILLA1+ SAFARI1+
//------------------------------------------------------------------------------------------------
function __fn_apply (o /* object */, a /* [args] */ )
{
    var u, s = '';
    if (a && a.constructor == Array) for (var i = 0, l = a.length, n = l - 1; i < l; ++i)
    {
        s += 'a[' + i + ']';
  
        if (i < n)
            s+= ',';
    }
 
    if (o == null)
        s = eval('this(' + s + ')');
  
    else
    {
        o.__callback = this;
        s = eval('o.__callback(' + s + ')');
        o.__callback = u;
    }
 
    return s;
}

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

if (!Function.prototype.apply) Function.prototype.apply = __fn_apply;

lunes, 1 de julio de 2013

Diálogos modales en JavaScript

Muestra un cuadro de diálogo modal con los parámetros especificados:
//------------------------------------------------------------------------------------------------
// Engines      JavaScript   1.4+
//              JScript      5.5+
// Environments NN6+ IE5.5+ MOZILLA1+ SAFARI1+
//------------------------------------------------------------------------------------------------
function getModalDlg
(
       s // url
     , c // callback
     , w // width
     , h // height
     , x // left
     , y // top
){
     if (s == null || (s += '').length < 1 || !/^((ht|f)tp(s)*\:\/\/)*\S+$/.test(s))
         throw new Error('getModalDlg: Invalid Url! "' + s + '"');

     if (typeof c != 'function')
         throw new Error('getModalDlg: Invalid callback function!');

     var a = Array.prototype.slice.call(arguments, 2);
     var b = a.length < 3;
     var f = ['width', 'height', 'left', 'top'];
 
     w = window;

     var p = w.showModalDialog;
     var d = p ? [':', 'px;'] : ['=', ','];
     var u = 'undefined';
     var m = typeof netscape != u;
     var n = navigator.userAgent;

     for (var i = 0; i < a.length; ++i)
         a[i] = (p ? 'dialog' : '') + f[i] + d[0] + a[i] + d[1];

     f = a.join('');

     if (p)
     {
         f += 'center:' + (b ? '1' : '0') + ';scroll:1;';
         f += n.indexOf('MSIE') + 1 ? 'status:0' : 'resizable:0';
         s  = p(s, null, f);
         
         c(typeof s.returnValue != u ? s.returnValue : s);
     }

     else
     {
         w.dlgcallback = c;
         f += 'dialog,scrollbars,chrome,modal=yes,location=0,status=0,toolbar=no';

         if (b) 
             f += ',centerscreen';

         if (m && (m = netscape.security.PrivilegeManager))
         { 
             try
             {
                 m.enablePrivilege(d = 'UniversalBrowserWrite');
                 w.open(s, '_blank', f);
                 m.disablePrivilege(d);

                 return;
             }

             catch (e) {}
         }

         if (typeof w.__dlg == u || w.__dlg.closed)
         {
             w.__dlg  = w.open(s, '_blank', f);

             try
             {
                 w.onfocus = function ()
                 {
                     if (typeof this.__dlg != u && !this.__dlg.closed)
                     {
                         this.blur();
                         this.__dlg.focus();
                     }

                     else delete this.onfocus;
                 }
 
                 w.__dlg.onblur = function ()
                 {
                     this.opener.blur();
                     this.focus();
                 }
             }

             catch (e)
             {
                 c    = { callback: c, dialog: w.__dlg};
                 c.id = setInterval
                 (
                       function (o)
                       {
                           var s = 'returnValue';
                           if (typeof o.dialog[s] != 'undefined')
                           {
                               o.callback(o.dialog[s]);
                               clearInterval(o.id);
                           }
                      }
                    , 0
                    , c
                );
             }
         }

         else if (typeof w.__dlg != u)
             w.__dlg.focus();
     }
}
Uso:
getModalDlg('my-url', function (returnValue) { /* TODO... */ }, 320, 240);
Código a insertar en la página del cuadro de diálogo:
function setDlgReturnValue (v /* value */)
{
     var s = 'returnValue';
     var w = window;

     if (w.showModalDialog || !w.opener || !w.opener.dlgcallback)
         w[s] = v;

     else w.opener.dlgcallback(v);
}
Uso:
setDlgReturnValue('my-value');
window.close();