#!/usr/bin/env python """ Test/utility code for moto-sync plugin, independent of opensync """ import sys, types, os.path, popen2 from optparse import OptionParser try: import motosync except ImportError: # motosync wasn't in our standard import path # try looking in the opensync python plugin dir for it child = popen2.Popen3('pkg-config opensync-1.0 --variable=libdir') libdir = child.fromchild.readline().rstrip('\n') if child.wait() != 0 or not os.path.isdir(libdir): # no pkgconfig, try standard install path libdir = '/usr/lib' if not os.path.isdir(os.path.join(libdir, 'opensync')): sys.stderr.write("Error: couldn't locate OpenSync library directory\n") sys.exit(1) sys.path.append(os.path.join(libdir, 'opensync', 'python-plugins')) import motosync DEFAULT_DEVICE = '/dev/ttyACM0' def parse_args(): p = OptionParser(version=motosync.__revision__, description='moto-sync test utility') p.add_option('-d', '--device', dest='device', help='device to access phone, defaults to %s' % DEFAULT_DEVICE) p.add_option('-t', '--type', action='append', dest='objtype', choices=motosync.SUPPORTED_OBJTYPES, help='object type to access (defaults to all types)') p.add_option('-f', '--file', dest='filename', help='name of backup/restore data file') p.add_option('--backup', action='store_const', dest='mode', const='backup', help='backup entries from the phone to a file') p.add_option('--restore', action='store_const', dest='mode', const='restore', help='restore a backup created with the --backup option') p.add_option('--delete', action='store_const', dest='mode', const='delete', help='delete all entries on the phone') p.set_defaults(device=DEFAULT_DEVICE, filename=None, mode=None, objtype=None) options, args = p.parse_args() if not options.mode: p.error('one of the backup, restore, or delete actions is required') if options.mode in ['backup', 'restore'] and not options.filename: p.error('this action requires a --file argument') return options def prompt_user(options): """Prompt the user if they are trying to write to the phone.""" if options.mode == 'delete' or options.mode == 'restore': print ('WARNING: About to %s all %s entries on the phone!' % (options.mode, ' & '.join(options.objtype))) print 'Are you sure? [yn] ', if sys.stdin.read(1).lower() != 'y': print 'Operation aborted' sys.exit(1) def pack_backup(typestr, edata): """pack event data into a single-line string to write to the backup file""" strings = [] for val in edata: if type(val) == types.IntType: strings.append(str(val)) else: if val == '': strings.append('') else: s = val.encode('utf8') # escape \ to \\, " to \" and newline to \n s = s.replace('\\', '\\\\') s = s.replace('"', '\\"') s = s.replace('\n', '\\n') strings.append('"' + s + '"') return ','.join([typestr] + strings) + '\n' def unpack_backup(line): """reverse the pack_backup function above""" # FIXME: is the unescaping sane with arbitrary utf8 characters? if line[-1] == '\n': line = line[:-1] parts = [] nextpart = '' wasquote = False inquote = False inescape = False for c in line: if c == ',' and not inquote: if not wasquote and len(parts) > 0 and nextpart != '': nextpart = int(nextpart) if wasquote: nextpart = nextpart.decode('utf8') parts.append(nextpart) nextpart = '' wasquote = False elif c == '\\' and inquote and not inescape: inescape = True elif inquote and inescape: inescape = False if c == 'n': c = '\n' else: assert(c == '"' or c == '\\') nextpart = nextpart + c elif c == '"': wasquote = True inquote = not inquote else: nextpart = nextpart + c if wasquote or nextpart == '': parts.append(nextpart.decode('utf8')) else: parts.append(int(nextpart)) return parts[0], parts[1:] def main(): options = parse_args() if not options.objtype: options.objtype = motosync.SUPPORTED_OBJTYPES prompt_user(options) pc = motosync.PhoneComms(options.device) pc.connect() if options.mode == 'delete' or options.mode == 'restore': if 'event' in options.objtype: for (edata, _) in pc.read_events(): pc.delete_event(edata[0]) if 'contact' in options.objtype: pc.read_contact_params() for edata in pc.read_contacts(): pc.delete_contact(edata[0]) if options.mode == 'backup': f = open(options.filename, 'w') if 'event' in options.objtype: for (edata, exceptions) in pc.read_events(): f.write(pack_backup('E', edata)) if exceptions != []: f.write(pack_backup('X', exceptions)) if 'contact' in options.objtype: pc.read_contact_params() for edata in pc.read_contacts(): f.write(pack_backup('C', edata)) f.close() if options.mode == 'restore': events = [] contacts = [] f = open(options.filename, 'r') for line in f.readlines(): typestr, edata = unpack_backup(line) if typestr == 'E': events.append((edata, [])) elif typestr == 'X': events[-1] = (events[-1][0], edata) elif typestr == 'C': contacts.append(edata) else: assert(False, 'Unexpected type %s' % typestr) f.close() if 'event' in options.objtype: for (e, x) in events: pc.write_event(e, x) if 'contact' in options.objtype: for e in contacts: pc.write_contact(e) pc.disconnect() if __name__ == "__main__": main()