Bluetooth Flooder per Symbian – 24/08/2009

Screen shotQuesta applicazione è stata creata da me per puro scopo didattico. Dovevo imparare ad utilizzare il bluetooth in Python e ho finito per creare questa simpatica applicazione.

Descrizione:
Una volta selezionato un file presente sul vostro cellulare, BT Flooder tenterà di inviarlo a tutte le periferiche bluetooth che sono nei paragi, tenendo una conta delle vittime che accettano il vostro file.
L’applicazione può essere utile per inviare un’immagine, magari un vostro sticker o una foto che volete condividere con tutti, oppure una semplice nota testo contenente un messaggio, un indirizzo di un sito internet, o qualsiasi cosa vi venga in mente.
L’applicazione supporta inoltre un filtro che permette di esonerare determinate periferiche dall’assillante invito a ricevere un file, e una funzione di logging, tramite la quale potrete conoscere, al termine dell’esecuzione, tutte le periferiche che hanno accettato il vostro file.

BT Flooder è stato scritto in Python, quindi necessita (ovviamente) che l’interprete sia installato sul vostro cellulare. Oltre a questo l’applicazione fa uso del modulo esterno LightBlue, necessario per cercare periferiche bluetooth silenziosamente. Il file di installazione è scaricabile a questo indirizzo, e va installato nella memoria interna, non sulla memory card.

– Download BT Flooder (versione .sis) –
– Download BT Flooder (versione .py) –
– Download modulo LightBlue –

Aggiornamento – 24/08/2009

Ho aggiornato l’applicazione perché presentava un bug nella codifica UTF-8. Non era infatti possibile selezionare periferiche e file con caratteri non-ASCII.

Segue il codice sorgente dell’applicazione.

import os
import e32
import sys
import socket
import appuifw
import globalui
import graphics

try:
    import lightblue
except:    
    appuifw.note(u'Modulo lightblue non trovato.', 'error')    
    if appuifw.query(u'Scaricare il modulo richiesto?', 'query'):
        url = 'http://lightblue.sourceforge.net/#downloads'
        browser = 'BrowserNG.exe'
        e32.start_exe(browser, ' "4 %s"' % url, 1)
    sys.exit()

#Variabili
log_file = u'C:\\bt_flooder_log.txt'
log_ok = False
accept = 0 #Persone che accettano
attemp = 0 #Tentativi di invio
filtro = [] #Filtro di MAC
devices = () #Periferiche
obj = u'Nessuno' #File selezionato
file_ok = False
status = u'Flooder in pausa.' #Stato flooder

#Draw
PAD = 5 #Padding
TXT = (0, 0, 0) #Colore testo
VAL = (255, 0, 0) #Colore valore
STATUS = (0, 200, 0) #Colore stato applicazione
ABOUT = (180, 180, 180) #Colore about
BACKGROUND = (240, 255, 255)
MRG = 25 #Margin
FONT_T = (None, 20)
FONT_V = (None, 20, 1)

def draw(r=None):
    """Disegna la schermata di stato"""
    global devices, status, attemp
    img.clear(BACKGROUND)
    #File oggetto
    img.text((PAD, MRG), u'File:', fill = TXT, font = FONT_T)
    X = img.measure_text(u'File: ', font = FONT_T)[1]
    img.text((PAD + X, MRG), u'%s' % obj[obj.rfind('\\')+1:], fill = VAL, font = FONT_V)
    #Log attivo/disattivo
    img.text((PAD, MRG * 2), u'Log:', fill = TXT, font = FONT_T)
    X = img.measure_text(u'Log: ', font = FONT_T)[1]
    img.text((PAD + X, MRG * 2), (log_ok and u'Attivato' or u'Disattivato'), fill = VAL, font = FONT_V)
    #Vittime che accettano
    img.text((PAD, MRG * 3), u'Vittime:', fill = TXT, font = FONT_T)
    X = img.measure_text(u'Vittime: ', font = FONT_T)[1]
    img.text((PAD + X, MRG * 3), u'%d' % accept, fill = VAL, font = FONT_V)
    #Tentativi di invio file
    img.text((PAD, MRG * 4), u'Tentativi invio:', fill = TXT, font = FONT_T)
    X = img.measure_text(u'Tentativi invio: ', font = FONT_T)[1]
    img.text((PAD + X, MRG * 4), u'%d' % attemp, fill = VAL, font = FONT_V)
    #Periferiche bluetooth nei paragi
    img.text((PAD, MRG * 5), u'Periferiche BT:', fill = TXT, font = FONT_T)
    X = img.measure_text(u'Periferiche BT: ', font = FONT_T)[1]
    img.text((PAD + X, MRG * 5), u'%d' % len(devices), fill = VAL, font = FONT_V)
    #Stato applicazione
    img.text((PAD, MRG * 6), status, fill = STATUS, font = FONT_V)
    #About
    img.text((PAD, MRG * 7 + 20), u'Created by Ale152', fill = ABOUT, font = FONT_T)
    img.text((PAD, MRG * 8 + 20), u'www.Wirgilio.it', fill = ABOUT, font = FONT_T)
    c.blit(img)

#Canvas
c = appuifw.Canvas(redraw_callback = draw)
img = graphics.Image.new(c.size)
appuifw.app.body = c

def list(path):
    """Lista una directory e restituisce un file selezionato"""
    #Torna indietro
    if path.endswith('\\..'): 
        #Mostra dischi
        if path in ['C:\\\\..', 'E:\\\\..']:
            files = ['C:', 'E:']
            path = ''
        #Mostra directory precedente
        else:
            path = path[:path.rfind('\\\\..')] #Elimino il \\.. dal path
            path = path[:path.rfind('\\')] #Elimino l'ultima dir dal path
            #Listo la dir
            files = os.listdir(path)
            files.sort()
            files[0:0] = ['\\..']
    #Lista directory
    elif path:
        files = os.listdir(path)
        files.sort()
        files[0:0] = ['\\..']
    ufiles = [i.decode('utf-8') for i in files] #appuifw richiede unicode
    try:
        selected = files[appuifw.selection_list(ufiles, 1)] #1 abilita ricerca
    except:
        #Nessun file selezionato
        pass
    #Se hai selezionato E: non deve restituire \E: !
    return path and path+'\\'+selected or selected


def select_file():
    """Seleziona un file da inviare alle vittime"""
    global log_ok, file_ok, obj
    appuifw.note(u'Selezionare un file da inviare alle vittime.')
    file_ok = False #File non selezionato
    path = 'C:\\\\..' #Torna indietro ai dischi
    temp = list(path)
    while not file_ok:
        temp = list(temp)      
        #Se è un file, restituisce l'indirizzo
        #Se è una cartella, la lista
        if os.path.isfile(temp):
            file_ok = True
            obj = temp.decode('utf-8')
            appuifw.note(u'File selezionato.', 'conf')
            draw()
            if log_ok:
                log.write('%s> file = %s\n' % (strftime('%H:%M:%S'), obj))

def logging():
    """Attiva o disattiva il file di log"""
    global log_ok, log, strftime
    log_ok = globalui.global_query(u'Salvare un file di log?')
    if log_ok:
        log = file(log_file, 'a')
        from time import strftime
        log.write('\n\nSession starts on %s\n' % strftime('%H:%M:%S'))
        appuifw.note(log_file+' creato.', 'conf')

#Chiede di attivare il logging all'avvio dell'applicazione        
logging()

#*****************
#     Filtro     #
#*****************
def filter(action):
    """Gestisce il filtro periferiche. Le periferiche nel filtro non verranno
    infastidite dal flooder"""
    global filtro
    #Aggiungi periferica
    if action == 'add':
        try:
            mac = socket.bt_obex_discover()[0]
            if mac:
                filtro.append(mac)
                appuifw.note(u'Periferica aggiunta.', 'conf')
            else:
                appuifw.note(u'Periferica non raggiungibile!', 'error')
        except:
            appuifw.note(u'Periferica non raggiungibile!', 'error')
    #Mostra periferiche
    elif action == 'show':
        if filtro:
            ufiltro = [i.decode('utf-8') for i in filtro]
            appuifw.selection_list(ufiltro)
        else:
            appuifw.note(u'Nessuna periferica nel filtro!', 'error')
    #Elimina periferiche
    elif action == 'del':
        if filtro:
            ufiltro = [i.decode('utf-8') for i in filtro]
            delete = appuifw.selection_list(ufiltro)
            filtro.remove(filtro[delete])
            appuifw.note(u'Periferica rimossa.', 'conf')
        else:
            appuifw.note(u'Nessuna periferica nel filtro!', 'error')

#About
def about():
    appuifw.query(u'BT Flooder created by Ale152.', 'query')
    appuifw.query(u'www.wirgilio.it', 'query')

#*****************
#  Applicazione  #
#*****************
def app(oper):
    global log_ok, file_ok, run, accept, obj, devices, status, attemp
    ##
    ## Run
    ##   
    if oper == 'run':
        run = True
        #Seleziona un file come oggetto
        if not file_ok:
            select_file()
        while run:
            #Cerco periferiche nei paragi
            status = u'Cerco periferiche...'
            draw()
            try:
                devices = lightblue.finddevices()
                if log_ok:
                    log.write('%s> %d devices found!\n' %
                             (strftime('%H:%M:%S'), len(devices)))
            except SymbianError:
                appuifw.note(u'Bluetooth disattivato!', 'error')
                app('stop')
                break
            #Cerco i servizi offerti
            for device in devices:
                try: #Supporta obex?
                    status = 'Verifico '+device[1]+'...'
                    draw()
                    #obex = (mac, {'srv':chan})
                    obex = socket.bt_obex_discover(device[0])
                    if obex and obex[0] not in filtro:
                        status = device[1]+' supporta OBEX!'
                        draw()
                        canale = obex[1].items()[0][1]
                        indirizzo = obex[0]
                        if log_ok:
                            log.write('%s> trying to send "%s" to %s (%s)...' %
                                     (strftime('%H:%M:%S'), obj, indirizzo, str(device[1])))
                        status = u'Provo ad inviare il file...'
                        attemp += 1
                        draw()
                        try: #Provo ad inviare il file
                            socket.bt_obex_send_file(indirizzo, canale, obj)
                            accept += 1
                            status = u'File inviato!'
                            draw()
                            e32.ao_sleep(1)
                            if log_ok:
                                log.write(' file sent! (%d)\n' % accept)
                        except:
                            if log_ok:
                                log.write(' file refused!\n')
                            status = u'File rifiutato :('
                            draw()
                except: #Non supporta obex. 
                    status = device[1]+' non supportato.'
                    draw()
            e32.ao_sleep(0.01)
    ##
    ## Stop
    ##   
    elif oper == 'stop':
        run = False
        status = u'Flooder in pausa.'
        draw()
    ##
    ## Quit
    ##   
    elif oper == 'quit':
        run = False
        #Chiude il log se aperto
        if log_ok:
            log.write('%s> victims = %d' % (strftime('%H:%M:%S'), accept))
            log.write('\nSession ends on %s\n\n' % strftime('%H:%M:%S'))
            log.close()
        lock.signal()
        appuifw.app.set_exit()
        
#Appuifw
appuifw.app.title = u'BT Flooder'
appuifw.app.exit_key_handler = lambda: app('quit')
appuifw.app.menu = [(u'Flooder', ((u'Avvia', lambda: app('run')),
                                  (u'Arresta', lambda: app('stop')),
                                  (u'Seleziona file', select_file))
                                 ),
                    (u'Filtro periferiche', ((u'Mostra', lambda: filter('show')),
                                             (u'Aggiungi', lambda: filter('add')),
                                             (u'Elimina', lambda: filter('del'))
                                            )),
                    (u'File log', logging),
                    (u'About', about),
                    (u'Esci', lambda: app('quit'))]
lock = e32.Ao_lock()
lock.wait()