Hola a todos. Quiero darles una bienvenida (algo tardía) a todos los integrantes de esta genial comunidad al nuevo curso. Les cuento que a pesar de no estar físicamente en la Universidad actualmente sigo siendo parte de la comunidad de Software Libre y seguiré publicando en la medida que mi trabajo me lo permita.

Hoy quiero compartir con ustedes una solución Home Made a un problema bastante común. Seguro muchos utilizan hoy en día redes wifi de bastante mala calidad. Es muy común que se nos desconecten los dispositivos o se “caiga” la red en cualquier momento lo cual puede ser un dolor de cabeza a la hora de hacer copias de ficheros. La mayoría de las herramientas que permiten hacer copias no están preparadas para una red tan hostil y no contemplan mecanismos de recuperación ante los errores lo que conlleva a que tengamos que empezar a copiar muchas veces desde el principio archivos que ya teníamos descargados a medias, consumiendo mucho más tiempo y recursos.

cifs

La herramienta que hoy les traigo como propuesta la programé para realizar la copia de archivos en una red inestable desde servidores cifs/smb (archivos compartidos de windows). Está escrita en groovy y depende de la librería jcifs de la cual ya les estuve hablando en una ocasión aquí. Hasta el momento no tiene una UI basada en formularios; es una aplicación de línea de comandos.

Para  comenzar al utilizar la aplicación debe especificar la ruta del archivo o carpeta que deseamos copiar utilizando el formato smb://servidor/archivo. Después debe especificar la ruta local donde desea guardar el mismo, selecciona si el servidor requiere autenticación y de ser positivo especificar las credenciales. Estos datos son guardados en texto plano en un fichero de extensión .magneto_task en el directorio donde se guardará la copia y comienza la descarga. En caso de ocurrir algún problema durante la descarga el servicio se queda tratando de lograr conexión nuevamente. Puede incluso apagar su computadora y reiniciar nuevamente la descarga cuando quiera. Puede ejecutar la aplicación sin parámetros para que la misma pregunte cada una de las especificaciones para la descarga o pasarle como argumento la dirección de un fichero de descarga .magneto_task para que continúe la descarga.

Bien, vamos al código;

#!/usr/bin/env groovy

package smbdownload

import java.io.File
import jcifs.smb.NtlmPasswordAuthentication
import jcifs.smb.SmbFile
import jcifs.smb.SmbFileInputStream

/**
 * returns the percent between the lengths of two files
 **/
def percent(file1,file2){
    Math.round(file1.length() / file2.length() * 100)
}

/**
 * prints the progress bar
 **/
def printProgress(percent){
    print "["
    (0..Math.round(percent / 10)).each{
        print "|"
    }

    (Math.round(percent/10)..10).each{
        print " "
    }
    
    print "] ${percent}% "
    print "\r"
}


/**
 * downloads a SmbFile to a Path
 * only used to download files, not directories 
 **/
def downloadFile(SmbFile file, String path){
    try{
        file.connect()
        def newfile = new File(path+"/"+file.getName())
        def f = new FileOutputStream(newfile,true)
        def is = new BufferedInputStream(file.getInputStream())
        
        def buf = new byte[1024*90]
        println "\ndownloading..."
        is.skip(newfile.length())
        
        readf = is.read(buf)
        while (readf > 0){
            printProgress(percent(newfile,file))
            f.write(buf,0,readf)
            readf = is.read(buf)
        }
        f.close()
        println "[||||||||||||] 100%"
        println "[ok]"
        return true
    } catch (Exception e){
        println e.getMessage()
        return false
    }
}

/**
 * lists all files on a base directory
 **/
def getFilesFromDir(SmbFile baseDir) {
    SmbFile[] files = baseDir.listFiles()
    List results = new ArrayList()
   
    for (SmbFile file : files) {
        if (file.isDirectory()) {
            results.addAll(getFilesFromDir(file))
        } else {
            results.add(file)
        }
    }
    return results
}


/**
 * writes the configuration file with .magneto_task extension
 * in order to resume the task
 **/
def writeConf(url, pathToSave, ua, user = "", password = ""){
    String aux = (String)url
    
    while (aux.endsWith("/")){
        aux = aux.substring(0,aux.length()-1)
    }
    aux = aux.substring(aux.lastIndexOf("/"),aux.length())
    println "writing config to "+pathToSave+aux+".magneto_task"
    File fo = new File(pathToSave+"/"+aux+".magneto_task")

    fo.append(url+"\n")
    fo.append(pathToSave+"\n")
    fo.append(ua+"\n")
    if (ua == "y"){
        fo.append(user+"\n")
        fo.append(password+"\n")
    }
    
}

public static void main (args) {
    println "Magneto v1.2 by \$h@"
    println "download from smb servers without loosing track of your files\n"
    Console sc = System.console()
    String url = ""
    def pathToSave = "."
    def ua = "n"
    def user = ""
    def password = ""
    if (args.length > 0){
        File file = new File(args[0])

        try {
            BufferedReader br = new BufferedReader(new FileReader(file))
            
            url = br.readLine()
            pathToSave = br.readLine()
            ua = br.readLine()
            if (ua == "y"){
                user = br.readLine()
                password = br.readLine()            
            }
            br.close();

        } catch (IOException e) {
        }
    } else {
        url = sc.readLine("url: ")
        pathToSave = sc.readLine("path to save: ")
        ua = sc.readLine("use auth (y/n): ")
    }
    
    SmbFile file = null

    if (ua.equals("y")){
        if (user == ""){
            user = sc.readLine("user: ")
        }
        if (password == ""){
            password = new String(sc.readPassword("Password: "))
        }
        NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("", user, password)
        file = new SmbFile(url,auth);
    } else {
        file = new SmbFile(url);
    }
    
    if (args.length <= 0){
        writeConf(url,pathToSave,ua,user,password)
    }
    
    try{
        if (!file.exists()){
            println "can't find file ${url}"
            System.exit(1)
        }
        if (file.isDirectory()) {
            print "listing files...\t"
            def filelist = getFilesFromDir(file)
            println "[ok]"
        
            print "populating dirs...\t"
        
            pathToSave += file.getName()
            new File(pathToSave).mkdirs()
            for (SmbFile f in filelist){
                new File(pathToSave+"/"+f.getParent().replace(url,"")).mkdirs()
            }
            println "[ok]"
        
            def c = 0
            for (SmbFile f in filelist){
                println "file ${++c} of ${filelist.size}"
                println "starting download for: ${f.getPath()}"
                while (!downloadFile(f,pathToSave+"/"+f.getParent().replace(url,""))){
                    sleep(100)
                }
            }
            
            println "[all done]"
        
        } else if (file.isFile()){
            println "starting download for: ${file.getName()}"
            while (!downloadFile(file,pathToSave)){
                sleep(100)
            }
        }
    } catch (jcifs.smb.SmbException e){
        println "${e.message} [failed]"
    } catch (java.net.UnknownHostException e){
        println "Unknow host [failed]"
    }
}

Es algo extenso el código como pueden ver, pero aún así decidí compartirlo completo para que pueda ser consultado fácilmente desde el mismo navegador. Pueden ejecutarlo de varias formas. teniendo groovy instalado y la librería jcifs en el classpath de java podemos ejecutar desde el terminal ./magneto.groovy lo cual iniciará la aplicación. Si desea tener la aplicación compilada en un .jar para poder compartirla con amigos que no tengan groovy instalado pueden copiar el código dentro de un proyecto de netbeans, compilar como si se tratara de un proyecto de aplicación en consola de java y listo.

Y bien, esto fue todo por hoy. Es un placer estar por aquí de nuevo. Espero que la comunidad siga creciendo y mejorando. Happy Hacking!!!