Ejemplo n.º 1
0
def merge_existing_contacts(source_contact, target_contact, delete_source_contact):
    # create temp files for each vcard
    # source vcard
    source_tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    source_temp_file_name = source_tf.name
    source_tf.write("# merge from %s\n%s" \
            % (source_contact.get_full_name(), helpers.get_existing_contact_template(source_contact)))
    source_tf.close()

    # target vcard
    target_tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    target_temp_file_name = target_tf.name
    target_tf.write("# merge into %s\n%s" \
            % (target_contact.get_full_name(), helpers.get_existing_contact_template(target_contact)))
    target_tf.close()

    # start editor to edit contact template
    child = subprocess.Popen([Config().get_merge_editor(), source_temp_file_name, target_temp_file_name])
    streamdata = child.communicate()[0]

    # template of source vcard is not required anymore
    os.remove(source_temp_file_name)

    # instead we are interested in the target template contents
    target_tf = open(target_temp_file_name, "r")
    merged_contact = CarddavObject.from_existing_contact_with_new_user_input(target_contact, target_tf.read())
    target_tf.close()
    os.remove(target_temp_file_name)

    # compare them
    if target_contact == merged_contact:
        print("Merge unsuccessfull: Target contact was not modified")
        return

    while True:
        if delete_source_contact:
            input_string = raw_input(
                    "Merge contact %s from address book %s into contact %s from address book %s\n\n" \
                        "To be removed\n\n%s\n\nMerged\n\n%s\n\nAre you sure? (y/n): " \
                    % (source_contact.get_full_name(), source_contact.get_address_book().get_name(),
                        merged_contact.get_full_name(), merged_contact.get_address_book().get_name(),
                        source_contact.print_vcard(), merged_contact.print_vcard()))
        else:
            input_string = raw_input(
                    "Merge contact %s from address book %s into contact %s from address book %s\n\n" \
                        "Keep unchanged\n\n%s\n\nMerged:\n\n%s\n\nAre you sure? (y/n): " \
                    % (source_contact.get_full_name(), source_contact.get_address_book().get_name(),
                        merged_contact.get_full_name(), merged_contact.get_address_book().get_name(),
                        source_contact.print_vcard(), merged_contact.print_vcard()))
        if input_string.lower() in ["", "n", "q"]:
            print("Canceled")
            return
        if input_string.lower() == "y":
            break

    # save merged_contact to disk and delete source contact
    merged_contact.write_to_file(overwrite=True)
    if delete_source_contact:
        source_contact.delete_vcard_file()
    print("Merge successful\n\n%s" % merged_contact.print_vcard())
Ejemplo n.º 2
0
def modify_existing_contact(old_contact):
    # create temp file and open it with the specified text editor
    tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    temp_file_name = tf.name
    tf.write("# Edit contact: %s\n%s" \
            % (old_contact.get_full_name(), helpers.get_existing_contact_template(old_contact)))
    tf.close()

    # start editor to edit contact template
    child = subprocess.Popen([Config().get_editor(), temp_file_name])
    streamdata = child.communicate()[0]

    # read temp file contents after editing
    tf = open(temp_file_name, "r")
    new_contact = CarddavObject.from_existing_contact_with_new_user_input(
        old_contact, tf.read())
    tf.close()
    os.remove(temp_file_name)

    # check if the user changed anything
    if old_contact == new_contact:
        print("Nothing changed.")
    else:
        new_contact.write_to_file(overwrite=True)
        print("Modification successful\n\n%s" % new_contact.print_vcard())
Ejemplo n.º 3
0
def create_new_contact(address_book):
    # create temp file
    tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    temp_file_name = tf.name
    old_contact_template = "# create new contact\n%s" % helpers.get_new_contact_template(address_book.get_name())
    tf.write(old_contact_template)
    tf.close()

    # start vim to edit contact template
    child = subprocess.Popen([Config().get_editor(), temp_file_name])
    streamdata = child.communicate()[0]

    # read temp file contents after editing
    tf = open(temp_file_name, "r")
    new_contact_template = tf.read()
    tf.close()
    os.remove(temp_file_name)

    # create carddav object from temp file
    if old_contact_template == new_contact_template:
        print("Canceled")
    else:
        new_contact = CarddavObject.from_user_input(address_book, new_contact_template)
        new_contact.write_to_file()
        print("Creation successful\n\n%s" % new_contact.print_vcard())
Ejemplo n.º 4
0
def create_new_contact(address_book):
    # create temp file
    tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    temp_file_name = tf.name
    old_contact_template = "# create new contact\n%s" % helpers.get_new_contact_template(
        address_book.get_name())
    tf.write(old_contact_template)
    tf.close()

    # start vim to edit contact template
    child = subprocess.Popen([Config().get_editor(), temp_file_name])
    streamdata = child.communicate()[0]

    # read temp file contents after editing
    tf = open(temp_file_name, "r")
    new_contact_template = tf.read()
    tf.close()
    os.remove(temp_file_name)

    # create carddav object from temp file
    if old_contact_template == new_contact_template:
        print("Canceled")
    else:
        new_contact = CarddavObject.from_user_input(address_book,
                                                    new_contact_template)
        new_contact.write_to_file()
        print("Creation successful\n\n%s" % new_contact.print_vcard())
Ejemplo n.º 5
0
def modify_existing_contact(old_contact):
    # create temp file and open it with the specified text editor
    tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    temp_file_name = tf.name
    tf.write("# Edit contact: %s\n%s" \
            % (old_contact.get_full_name(), old_contact.get_template()))
    tf.close()

    temp_file_creation = helpers.file_modification_date(temp_file_name)
    while True:
        # start editor to edit contact template
        child = subprocess.Popen([Config().get_editor(), temp_file_name])
        streamdata = child.communicate()[0]
        if temp_file_creation == helpers.file_modification_date(temp_file_name):
            print "not modified"
            new_contact = None
            os.remove(temp_file_name)
            break

        # read temp file contents after editing
        tf = open(temp_file_name, "r")
        new_contact_template = tf.read()
        tf.close()

        # try to create contact from user input
        try:
            new_contact = CarddavObject.from_existing_contact_with_new_user_input(
                    old_contact, new_contact_template)
        except ValueError as e:
            print("\n%s\n" % e)
            while True:
                input_string = raw_input("Do you want to open the editor again (y/n)? ")
                if input_string.lower() in ["", "n", "q"]:
                    print("Canceled")
                    os.remove(temp_file_name)
                    sys.exit(0)
                if input_string.lower() == "y":
                    break
        else:
            os.remove(temp_file_name)
            break

    # check if the user changed anything
    if new_contact is None \
            or old_contact == new_contact:
        print("Nothing changed\n\n%s" % old_contact.print_vcard())
    else:
        new_contact.write_to_file(overwrite=True)
        print("Modification successful\n\n%s" % new_contact.print_vcard())
Ejemplo n.º 6
0
def create_new_contact(address_book):
    # create temp file
    tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    temp_file_name = tf.name
    old_contact_template = "# create new contact\n%s" % helpers.get_new_contact_template(address_book.get_name())
    tf.write(old_contact_template)
    tf.close()

    temp_file_creation = helpers.file_modification_date(temp_file_name)
    while True:
        # start vim to edit contact template
        child = subprocess.Popen([Config().get_editor(), temp_file_name])
        streamdata = child.communicate()[0]
        if temp_file_creation == helpers.file_modification_date(temp_file_name):
            new_contact = None
            os.remove(temp_file_name)
            break

        # read temp file contents after editing
        tf = open(temp_file_name, "r")
        new_contact_template = tf.read()
        tf.close()

        # try to create new contact
        try:
            new_contact = CarddavObject.from_user_input(address_book, new_contact_template)
        except ValueError as e:
            print("\n%s\n" % e)
            while True:
                input_string = raw_input("Do you want to open the editor again (y/n)? ")
                if input_string.lower() in ["", "n", "q"]:
                    print("Canceled")
                    os.remove(temp_file_name)
                    sys.exit(0)
                if input_string.lower() == "y":
                    break
        else:
            os.remove(temp_file_name)
            break

    # create carddav object from temp file
    if new_contact is None \
            or old_contact_template == new_contact_template:
        print("Canceled")
    else:
        new_contact.write_to_file()
        print("Creation successful\n\n%s" % new_contact.print_vcard())
Ejemplo n.º 7
0
def create_new_contact(addressbook):
    # create temp file
    tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    temp_file_name = tf.name
    tf.write(helpers.get_new_contact_template(addressbook['name']))
    tf.close()
    # start vim to edit contact template
    child = subprocess.Popen([Config().get_editor(), temp_file_name])
    streamdata = child.communicate()[0]
    # read temp file contents after editing
    tf = open(temp_file_name, "r")
    new_contact_template = tf.read()
    tf.close()
    os.remove(temp_file_name)
    # create carddav object from temp file
    vcard = CarddavObject(addressbook['name'], addressbook['path'])
    vcard.process_user_input(new_contact_template)
    vcard.write_to_file()
    print "Creation successful\n\n%s" % vcard.print_vcard()
Ejemplo n.º 8
0
def modify_existing_contact(old_contact):
    # create temp file and open it with the specified text editor
    tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    temp_file_name = tf.name
    tf.write("# Edit contact: %s\n%s" \
            % (old_contact.get_full_name(), helpers.get_existing_contact_template(old_contact)))
    tf.close()

    # start editor to edit contact template
    child = subprocess.Popen([Config().get_editor(), temp_file_name])
    streamdata = child.communicate()[0]

    # read temp file contents after editing
    tf = open(temp_file_name, "r")
    new_contact = CarddavObject.from_existing_contact_with_new_user_input(old_contact, tf.read())
    tf.close()
    os.remove(temp_file_name)

    # check if the user changed anything
    if old_contact == new_contact:
        print("Nothing changed.")
    else:
        new_contact.write_to_file(overwrite=True)
        print("Modification successful\n\n%s" % new_contact.print_vcard())
Ejemplo n.º 9
0
def create_new_contact(addressbook):
    # create temp file
    tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    temp_file_name = tf.name
    tf.write(helpers.get_new_contact_template(addressbook['name']))
    tf.close()
    # start vim to edit contact template
    child = subprocess.Popen([Config().get_editor(), temp_file_name])
    streamdata = child.communicate()[0]
    # read temp file contents after editing
    tf = open(temp_file_name, "r")
    new_contact_template = tf.read()
    tf.close()
    os.remove(temp_file_name)
    # create carddav object from temp file
    vcard = CarddavObject(addressbook['name'], addressbook['path'])
    vcard.process_user_input(new_contact_template)
    vcard.write_to_file()
    print "Creation successful\n\n%s" % vcard.print_vcard()
Ejemplo n.º 10
0
        def __init__(self):
            self.config = None
            self.address_book_list = []
            self.uid_dict = {}

            # load config file
            xdg_config_home = os.environ.get("XDG_CONFIG_HOME") or \
                    os.path.expanduser("~/.config")
            config_file = os.environ.get("KHARD_CONFIG") or \
                    os.path.join(xdg_config_home, "khard", "khard.conf")
            if os.path.exists(config_file) == False:
                print("Config file %s not available" % config_file)
                sys.exit(2)
            self.config = ConfigObj(config_file, interpolation=False)

            # general settings
            if self.config.has_key("general") == False:
                print("Error in config file\nMissing main section \"[general]\".")
                sys.exit(2)

            # editor
            self.config['general']['editor'] = self.config['general'].get("editor") \
                    or os.environ.get("EDITOR")
            if self.config['general']['editor'] is None:
                print("Error in config file\n" \
                        "Set path to your preferred text editor in khard's config file or the $EDITOR shell variable\n" \
                        "Example for khard.conf: editor = vim")
                sys.exit(2)
            self.config['general']['editor'] = find_executable(
                    os.path.expanduser(self.config['general']['editor']))
            if self.config['general']['editor'] is None:
                print("Error in config file\nInvalid editor path or executable not found.")
                sys.exit(2)

            # merge editor
            self.config['general']['merge_editor'] = self.config['general'].get("merge_editor") \
                    or os.environ.get("MERGE_EDITOR")
            if self.config['general']['merge_editor'] is None:
                print("Error in config file\n" \
                        "Set path to your preferred text merge editor in khard's config file or the $MERGE_EDITOR shell variable\n" \
                        "Example for khard.conf: merge_editor = vimdiff")
                sys.exit(2)
            self.config['general']['merge_editor'] = find_executable(
                    os.path.expanduser(self.config['general']['merge_editor']))
            if self.config['general']['merge_editor'] is None:
                print("Error in config file\nInvalid merge editor path or executable not found.")
                sys.exit(2)

            # default values for action and nickname settings
            if self.config['general'].has_key("default_action") == False:
                print("Error in config file\nMissing default action parameter.")
                sys.exit(2)
            elif self.config['general']['default_action'] not in self.get_list_of_actions():
                print("Error in config file\n" \
                        "Non existing value for default action parameter\n" \
                        "Possible values are: %s" % ', '.join(self.get_list_of_actions()))
                sys.exit(2)
            if self.config['general'].has_key("show_nicknames") == False:
                self.config['general']['show_nicknames'] = False
            elif self.config['general']['show_nicknames'] == "yes":
                self.config['general']['show_nicknames'] = True
            elif self.config['general']['show_nicknames'] == "no":
                self.config['general']['show_nicknames'] = False
            else:
                print("Error in config file\nshow_nicknames parameter must be yes or no.")
                sys.exit(2)

            # load address books and contacts
            error_counter = 0
            number_of_contacts = 0
            if self.config.has_key("addressbooks") == False:
                print("Error in config file\nMissing main section \"[addressbooks]\".")
                sys.exit(2)
            if len(self.config['addressbooks'].keys()) == 0:
                print("Error in config file\nNo address book entries available.")
                sys.exit(2)
            for name in self.config['addressbooks'].keys():
                # create address book object
                try:
                    address_book = AddressBook(name, self.config['addressbooks'][name]['path'])
                except KeyError as e:
                    print("Error in config file\nMissing path to the \"%s\" address book." % name)
                    sys.exit(2)
                except IOError as e:
                    print("Error in config file\n%s" % e)
                    sys.exit(2)

                # load all vcard files
                for filename in glob.glob(os.path.join(address_book.get_path(), "*.vcf")):
                    try:
                        address_book.add_contact(
                                CarddavObject.from_file(address_book, filename))
                        number_of_contacts += 1
                    except IOError as e:
                        print("Error: Could not open file %s\n%s" % (filename, e))
                        error_counter += 1
                    except vobject.base.ParseError as e:
                        print("Error: Could not parse file %s\n%s" % (filename, e))
                        error_counter += 1

                # add address book to list
                self.address_book_list.append(address_book)

            # check if one or more contacts could not be parsed
            if error_counter > 0:
                print("\n%d of %d vcard files could not be parsed" % (error_counter, number_of_contacts))
                sys.exit(2)

            # check, if multiple contacts have the same uid
            length_of_shortest_uid = 100
            number_of_contacts_with_uid = 0
            for address_book in self.address_book_list:
                for contact in address_book.get_contact_list():
                    uid = contact.get_uid()
                    if uid != "":
                        matching_contact = self.uid_dict.get(uid)
                        if matching_contact is None:
                            self.uid_dict[uid] = contact
                            number_of_contacts_with_uid += 1
                            if len(uid) < length_of_shortest_uid:
                                length_of_shortest_uid = len(uid)
                        else:
                            print("The contact %s from address book %s" \
                                    " and the contact %s from address book %s have the same uid %s" \
                                    % (matching_contact.get_full_name(),
                                        matching_contact.get_address_book().get_name(),
                                        contact.get_full_name(),
                                        contact.get_address_book().get_name(),
                                        contact.get_uid())
                                    )
                            sys.exit(2)

            # now we can be sure, that all uid's are unique but we don't want to enter
            # the whole uid, if we choose a contact by the -u / --uid option
            # so clear previously filled uid_dict and recreate with the shortest possible uid, so
            # that it's still unique and easier to enter
            # with around 100 contacts that short id should not be longer then two or three characters
            length_of_uid = 1
            while True:
                self.uid_dict.clear()
                for address_book in self.address_book_list:
                    for contact in address_book.get_contact_list():
                        uid = contact.get_uid()[:length_of_uid]
                        if uid != "":
                            self.uid_dict[uid] = contact
                if len(self.uid_dict.keys()) != number_of_contacts_with_uid:
                    length_of_uid += 1
                else:
                    break
                if length_of_uid == length_of_shortest_uid:
                    # prevent infinit loop, 
                    # should not be necessary, cause we checked the uid uniqueness in the previous step
                    # so it's just a precaution
                    print("Could not create the dictionary of the short uid's")
                    sys.exit(2)
Ejemplo n.º 11
0
        def __init__(self):
            self.config = None
            self.address_book_list = []

            # load config file
            xdg_config_home = os.environ.get("XDG_CONFIG_HOME") or \
                    os.path.expanduser("~/.config")
            config_file = os.environ.get("KHARD_CONFIG") or \
                    os.path.join(xdg_config_home, "khard", "khard.conf")
            if os.path.exists(config_file) == False:
                print("Config file %s not available" % config_file)
                sys.exit(2)
            self.config = ConfigObj(config_file, interpolation=False)

            # general settings
            if self.config.has_key("general") == False:
                print("Error in config file\nMissing main section \"[general]\".")
                sys.exit(2)

            # editor
            self.config['general']['editor'] = self.config['general'].get("editor") \
                    or os.environ.get("EDITOR")
            if self.config['general']['editor'] is None:
                print("Error in config file\n" \
                        "Set path to your preferred text editor in khard's config file or the $EDITOR shell variable\n" \
                        "Example for khard.conf: editor = vim")
                sys.exit(2)
            self.config['general']['editor'] = find_executable(
                    os.path.expanduser(self.config['general']['editor']))
            if self.config['general']['editor'] is None:
                print("Error in config file\nInvalid editor path or executable not found.")
                sys.exit(2)

            # merge editor
            self.config['general']['merge_editor'] = self.config['general'].get("merge_editor") \
                    or os.environ.get("MERGE_EDITOR")
            if self.config['general']['merge_editor'] is None:
                print("Error in config file\n" \
                        "Set path to your preferred text merge editor in khard's config file or the $MERGE_EDITOR shell variable\n" \
                        "Example for khard.conf: merge_editor = vimdiff")
                sys.exit(2)
            self.config['general']['merge_editor'] = find_executable(
                    os.path.expanduser(self.config['general']['merge_editor']))
            if self.config['general']['merge_editor'] is None:
                print("Error in config file\nInvalid merge editor path or executable not found.")
                sys.exit(2)

            # default values for action and nickname settings
            if self.config['general'].has_key("default_action") == False:
                print("Error in config file\nMissing default action parameter.")
                sys.exit(2)
            elif self.config['general']['default_action'] not in self.get_list_of_actions():
                print("Error in config file\n" \
                        "Non existing value for default action parameter\n" \
                        "Possible values are: %s" % ', '.join(self.get_list_of_actions()))
                sys.exit(2)
            if self.config['general'].has_key("show_nicknames") == False:
                self.config['general']['show_nicknames'] = False
            elif self.config['general']['show_nicknames'] == "yes":
                self.config['general']['show_nicknames'] = True
            elif self.config['general']['show_nicknames'] == "no":
                self.config['general']['show_nicknames'] = False
            else:
                print("Error in config file\nshow_nicknames parameter must be yes or no.")
                sys.exit(2)

            # load address books
            error_counter = 0
            if self.config.has_key("addressbooks") == False:
                print("Error in config file\nMissing main section \"[addressbooks]\".")
                sys.exit(2)
            if len(self.config['addressbooks'].keys()) == 0:
                print("Error in config file\nNo address book entries available.")
                sys.exit(2)
            for name in self.config['addressbooks'].keys():
                # create address book object
                try:
                    address_book = AddressBook(name, self.config['addressbooks'][name]['path'])
                except KeyError as e:
                    print("Error in config file\nMissing path to the \"%s\" address book." % name)
                    sys.exit(2)
                except IOError as e:
                    print("Error in config file\n%s" % e)
                    sys.exit(2)

                # load all vcard files
                for filename in glob.glob(os.path.join(address_book.get_path(), "*.vcf")):
                    try:
                        address_book.add_contact(
                                CarddavObject.from_file(address_book, filename))
                    except IOError as e:
                        print("Error: Could not open file %s\n%s" % (filename, e))
                        error_counter += 1
                    except vobject.base.ParseError as e:
                        print("Error: Could not parse file %s\n%s" % (filename, e))
                        error_counter += 1

                # add address book to list
                self.address_book_list.append(address_book)

            # check if one or more contacts could not be parsed
            if error_counter > 0:
                if error_counter == 1:
                    print("\n1 vcard file could not be parsed")
                elif error_counter > 1:
                    print("\n%d vcard files could not be parsed" % error_counter)
                sys.exit(2)
Ejemplo n.º 12
0
def main():
    # create the args parser
    parser = argparse.ArgumentParser(
            description = "Khard is a carddav address book for the console",
            formatter_class = argparse.RawTextHelpFormatter)
    parser.add_argument("-a", "--addressbook", default="",
            help="Specify address book names as comma separated list")
    parser.add_argument("-r", "--reverse", action="store_true", help="Sort contacts in reverse order")
    parser.add_argument("-s", "--search", default="",
            help="Search in all contact data fields\n" \
                    "    default:   -s \"contact\"\n" \
                    "    merge:     -s \"source contact,target contact\"\n" \
                    "    copy/move: -s \"source contact,target address book\"")
    parser.add_argument("-t", "--sort", default="alphabetical", 
            help="Sort contacts list. Possible values: alphabetical, addressbook")
    parser.add_argument("-v", "--version", action="store_true", help="Get current program version")
    parser.add_argument("action", nargs="?", default="",
            help="Possible actions:\n" \
                    "    list, details, email, phone, source,\n" \
                    "    new, add-email, merge, modify, copy, move and remove")
    args = parser.parse_args()

    # version
    if args.version == True:
        print("Khard version %s" % khard_version)
        sys.exit(0)

    # validate value for action
    if args.action == "":
        args.action = Config().get_default_action()
    if args.action not in Config().get_list_of_actions():
        print("Unsupported action. Possible values are: %s" % ', '.join(Config().get_list_of_actions()))
        sys.exit(1)

    # load address books which are defined in the configuration file
    selected_address_books = []
    if args.addressbook == "":
        selected_address_books = Config().get_all_address_books()
    else:
        for name in args.addressbook.split(","):
            if Config().get_address_book(name) is None:
                print("Error: The entered address book \"%s\" does not exist.\nPossible values are: %s" \
                        % (name, ', '.join([ str(book) for book in Config().get_all_address_books() ])))
                sys.exit(1)
            else:
                selected_address_books.append(Config().get_address_book(name))

    # search parameter
    # may either contain one search term for a standard search or two terms, devided by a "," to
    # search for two contacts to merge them
    search_terms = args.search.split(",")
    if len(search_terms) == 1:
        search_terms.append("")

    # sort criteria
    if args.sort not in ["alphabetical", "addressbook"]:
        print("Unsupported sort criteria. Possible values: alphabetical, addressbook")
        sys.exit(1)

    # create a list of all found vcard objects
    vcard_list = get_contact_list_by_user_selection(
            selected_address_books, args.sort, args.reverse, search_terms[0], False)

    # create new contact
    if args.action == "new":
        if len(selected_address_books) != 1:
            if args.addressbook == "":
                print("Error: You must specify an address book for the new contact\nPossible values are: %s" \
                        % ', '.join([ str(book) for book in Config().get_all_address_books() ]))
            else:
                print("Please enter only one address book name")
            sys.exit(1)
        # if there is some data in stdin
        if not sys.stdin.isatty():
            # create new contact from stdin
            new_contact = CarddavObject.from_user_input(
                    selected_address_books[0], sys.stdin.read())
            print(new_contact.print_vcard())
            new_contact.write_to_file()
        else:
            # open editor
            create_new_contact(selected_address_books[0])

    # add email address to contact or create a new one if necessary
    if args.action == "add-email":
        # get name and email address
        email_address = ""
        name = ""
        for line in sys.stdin:
            if line.startswith("From:"):
                try:
                    name = line[6:line.index("<")-1]
                    email_address = line[line.index("<")+1:line.index(">")]
                except ValueError as e:
                    email_address = line[6:].strip()
                break
        # reopen stdin to get user input
        sys.stdin = open('/dev/tty')
        print("Khard: Add email address to contact")
        if not email_address:
            print("Found no email address")
            sys.exit(1)
        print("Email address: %s" % email_address)
        if not name:
            name = raw_input("Contact's name: ")
        else:
            # remove quotes from name string, otherwise decoding fails
            name = name.replace("\"", "")
            # fix encoding of senders name
            name, encoding = decode_header(name)[0]
            if encoding:
                name = name.decode(encoding).encode("utf-8").replace("\"", "")
            # query user input.
            user_input = raw_input("Contact's name [%s]: " % name)
            # if empty, use the extracted name from above
            name = user_input or name

        # search for an existing contact
        selected_vcard = choose_vcard_from_list(
                get_contact_list_by_user_selection(
                    selected_address_books, args.sort, args.reverse, name, True))
        if selected_vcard is None:
            # create new contact
            while True:
                input_string = raw_input("Contact %s does not exist. Do you want to create it (y/n)? " % name)
                if input_string.lower() in ["", "n", "q"]:
                    print("Canceled")
                    sys.exit(0)
                if input_string.lower() == "y":
                    break
            # ask for address book, in which to create the new contact
            print("Available address books: %s" \
                    % ', '.join([ str(book) for book in Config().get_all_address_books() ]))
            while True:
                book_name = raw_input("Address book [%s]: " % selected_address_books[0].get_name()) \
                        or selected_address_books[0].get_name()
                if Config().get_address_book(book_name) is not None:
                    break
            # ask for name and organisation of new contact
            while True:
                first_name = raw_input("First name: ")
                last_name = raw_input("Last name: ")
                organization = raw_input("Organization: ")
                if not first_name and not last_name and not organization:
                    print("Error: All fields are empty.")
                else:
                    break
            selected_vcard = CarddavObject.new_contact(Config().get_address_book(book_name))
            selected_vcard.set_name_and_organisation("", first_name, last_name, organization, "")

        # check if the contact already contains the email address
        for email_entry in selected_vcard.get_email_addresses():
            if email_entry['value'] == email_address:
                print("The contact %s already contains the email address %s" % (selected_vcard, email_address))
                sys.exit(0)

        # ask for confirmation again
        while True:
            input_string = raw_input("Do you want to add the email address %s to the contact %s (y/n)? " \
                    % (email_address, selected_vcard.get_full_name()))
            if input_string.lower() in ["", "n", "q"]:
                print("Canceled")
                sys.exit(0)
            if input_string.lower() == "y":
                break

        # ask for the email label
        print("\nAdding email address %s to contact %s" % (email_address, selected_vcard))
        label = raw_input("email label [home]: ") or "home"
        # add email address to vcard object
        selected_vcard.add_email_address(label, email_address)
        # save to disk
        selected_vcard.write_to_file(overwrite=True)
        print("Done.\n\n%s" % selected_vcard.print_vcard())

    # print phone application  friendly contacts table
    if args.action == "phone":
        all_phone_numbers_list = []
        matching_phone_number_list = []
        regexp = re.compile(search_terms[0].replace("*", ".*").replace(" ", ".*"), re.IGNORECASE)
        for vcard in vcard_list:
            for tel_entry in vcard.get_phone_numbers():
                phone_number_line = "%s\t%s\t%s" \
                        % (tel_entry['value'], vcard.get_full_name(), tel_entry['type'])
                if len(re.sub("\D", "", search_terms[0])) >= 3:
                    # the user likely searches for a phone number cause the search string contains
                    # at least three digits
                    # so we remove all non-digit chars from the phone number field and match against that
                    if regexp.search(re.sub("\D", "", tel_entry['value'])) != None:
                        matching_phone_number_list.append(phone_number_line)
                else:
                    # the user doesn't search for a phone number so we can perform a standard search
                    # without removing all non-digit chars from the phone number string
                    if regexp.search(phone_number_line) != None:
                        matching_phone_number_list.append(phone_number_line)
                # collect all phone numbers in a different list as fallback
                all_phone_numbers_list.append(phone_number_line)
        if len(matching_phone_number_list) > 0:
            print('\n'.join(matching_phone_number_list))
        elif len(all_phone_numbers_list) > 0:
            print('\n'.join(all_phone_numbers_list))
        else:
            sys.exit(1)

    # print mail client friendly contacts table
    # compatible to mutt and alot
    # output format:
    #   single line of text
    #   email_address\tname\ttype
    #   email_address\tname\ttype
    #   [...]
    if args.action == "email":
        matching_email_address_list = []
        all_email_address_list = []
        regexp = re.compile(search_terms[0].replace("*", ".*").replace(" ", ".*"), re.IGNORECASE)
        for vcard in vcard_list:
            for email_entry in vcard.get_email_addresses():
                email_address_line = "%s\t%s\t%s" \
                        % (email_entry['value'], vcard.get_full_name(), email_entry['type'])
                if regexp.search(email_address_line) != None:
                    matching_email_address_list.append(email_address_line)
                # collect all email addresses in a different list as fallback
                all_email_address_list.append(email_address_line)
        print("searching for '%s' ..." % search_terms[0])
        if len(matching_email_address_list) > 0:
            print('\n'.join(matching_email_address_list))
        elif len(all_email_address_list) > 0:
            print('\n'.join(all_email_address_list))
        else:
            sys.exit(1)

    # print user friendly contacts table
    if args.action == "list":
        if len(vcard_list) == 0:
            print("Found no contacts")
            sys.exit(1)
        list_contacts(vcard_list)

    # show source or details, modify or remove contact
    if args.action in ["details", "modify", "remove", "source"]:
        selected_vcard = choose_vcard_from_list(vcard_list)
        if selected_vcard is None:
            print("Found no contact")
            sys.exit(1)

        if args.action == "details":
            print selected_vcard.print_vcard()

        elif args.action == "modify":
            modify_existing_contact(selected_vcard)

        elif args.action == "remove":
            while True:
                input_string = raw_input("Deleting contact %s from address book %s. Are you sure? (y/n): " \
                        % (selected_vcard.get_full_name(), selected_vcard.get_address_book().get_name()))
                if input_string.lower() in ["", "n", "q"]:
                    print("Canceled")
                    sys.exit(0)
                if input_string.lower() == "y":
                    break
            selected_vcard.delete_vcard_file()
            print("Contact deleted successfully")

        elif args.action == "source":
            child = subprocess.Popen([Config().get_editor(),
                    selected_vcard.get_filename()])
            streamdata = child.communicate()[0]

    # merge contacts
    if args.action == "merge":
        # get the source vcard, from which to merge
        source_vcard = choose_vcard_from_list(vcard_list)
        if source_vcard is None:
            print("Found no source contact for merging")
            sys.exit(1)

        # get the target vcard, into which to merge
        print("Merge from %s from address book %s\n\nNow choose the contact into which to merge:" \
                % (source_vcard.get_full_name(), source_vcard.get_address_book().get_name()))
        target_vcard = choose_vcard_from_list(
                get_contact_list_by_user_selection(
                    selected_address_books, args.sort, args.reverse, search_terms[1], False))
        if target_vcard is None:
            print("Found no target contact for merging")
            sys.exit(1)

        # merging
        if source_vcard == target_vcard:
            print("The selected contacts are already identical")
        else:
            merge_existing_contacts(source_vcard, target_vcard, True)

    # copy or move contact
    if args.action in ["copy", "move"]:
        # get the source vcard, which to copy or move
        source_vcard = choose_vcard_from_list(vcard_list)
        if source_vcard is None:
            print("Found no contact")
            sys.exit(1)

        # get target address book from search query if provided
        available_address_books = [ book for book in Config().get_all_address_books() if book != source_vcard.get_address_book() ]
        target_address_book = None
        if search_terms[1] != "":
            target_address_book = Config().get_address_book(search_terms[1])
            if target_address_book == None:
                print("The given target address book %s does not exist\n" % search_terms[1])
            elif target_address_book not in available_address_books:
                print("The contact %s is already in the address book %s" \
                        % (source_vcard.get_full_name(), target_address_book.get_name()))
                sys.exit(1)

        # otherwise query the target address book name from user
        if target_address_book == None:
            print("%s contact %s from address book %s\n\nAvailable address books:\n  %s" % (args.action.title(),
                    source_vcard.get_full_name(), source_vcard.get_address_book().get_name(),
                    '\n  '.join([ str(book) for book in available_address_books ])))
        while target_address_book is None:
            input_string = raw_input("Into address book: ")
            if input_string == "":
                print("Canceled")
                sys.exit(0)
            if Config().get_address_book(input_string) in available_address_books:
                print("")
                target_address_book = Config().get_address_book(input_string)

        # check if a contact already exists in the target address book
        target_vcard = choose_vcard_from_list(
                get_contact_list_by_user_selection(
                    [target_address_book], args.sort, args.reverse, source_vcard.get_full_name(), True))

        # if the target contact doesn't exist, move or copy the source contact into the target
        # address book without further questions
        if target_vcard is None:
            copy_contact(source_vcard, target_address_book, args.action == "move")
        else:
            if source_vcard == target_vcard:
                # source and target contact are identical
                if args.action == "move":
                    copy_contact(source_vcard, target_address_book, True)
                else:
                    print("The selected contacts are already identical")
            else:
                # source and target contacts are different
                # either overwrite the target one or merge into target contact
                print("The address book %s already contains the contact %s\n\n" \
                        "Source\n\n%s\n\nTarget\n\n%s\n\n" \
                        "Possible actions:\n" \
                        "  o: Overwrite target contact\n  m: merge from source into target contact\n  q: quit" \
                        % (target_vcard.get_address_book().get_name(), source_vcard.get_full_name(),
                            source_vcard.print_vcard(), target_vcard.print_vcard()))
                while True:
                    input_string = raw_input("Your choice: ")
                    if input_string.lower() == "o":
                        copy_contact(source_vcard, target_address_book, args.action == "move")
                        break
                    if input_string.lower() == "m":
                        merge_existing_contacts(source_vcard, target_vcard, args.action == "move")
                        break
                    if input_string.lower() in ["", "q"]:
                        print("Canceled")
                        break
Ejemplo n.º 13
0
def merge_existing_contacts(source_contact, target_contact,
                            delete_source_contact):
    # create temp files for each vcard
    # source vcard
    source_tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    source_temp_file_name = source_tf.name
    source_tf.write("# merge from %s\n%s" \
            % (source_contact.get_full_name(), helpers.get_existing_contact_template(source_contact)))
    source_tf.close()

    # target vcard
    target_tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    target_temp_file_name = target_tf.name
    target_tf.write("# merge into %s\n%s" \
            % (target_contact.get_full_name(), helpers.get_existing_contact_template(target_contact)))
    target_tf.close()

    # start editor to edit contact template
    child = subprocess.Popen([
        Config().get_merge_editor(), source_temp_file_name,
        target_temp_file_name
    ])
    streamdata = child.communicate()[0]

    # template of source vcard is not required anymore
    os.remove(source_temp_file_name)

    # instead we are interested in the target template contents
    target_tf = open(target_temp_file_name, "r")
    merged_contact = CarddavObject.from_existing_contact_with_new_user_input(
        target_contact, target_tf.read())
    target_tf.close()
    os.remove(target_temp_file_name)

    # compare them
    if target_contact == merged_contact:
        print("Merge unsuccessfull: Target contact was not modified")
        return

    while True:
        if delete_source_contact:
            input_string = raw_input(
                    "Merge contact %s from address book %s into contact %s from address book %s\n\n" \
                        "To be removed\n\n%s\n\nMerged\n\n%s\n\nAre you sure? (y/n): " \
                    % (source_contact.get_full_name(), source_contact.get_address_book().get_name(),
                        merged_contact.get_full_name(), merged_contact.get_address_book().get_name(),
                        source_contact.print_vcard(), merged_contact.print_vcard()))
        else:
            input_string = raw_input(
                    "Merge contact %s from address book %s into contact %s from address book %s\n\n" \
                        "Keep unchanged\n\n%s\n\nMerged:\n\n%s\n\nAre you sure? (y/n): " \
                    % (source_contact.get_full_name(), source_contact.get_address_book().get_name(),
                        merged_contact.get_full_name(), merged_contact.get_address_book().get_name(),
                        source_contact.print_vcard(), merged_contact.print_vcard()))
        if input_string.lower() in ["", "n", "q"]:
            print("Canceled")
            return
        if input_string.lower() == "y":
            break

    # save merged_contact to disk and delete source contact
    merged_contact.write_to_file(overwrite=True)
    if delete_source_contact:
        source_contact.delete_vcard_file()
    print("Merge successful\n\n%s" % merged_contact.print_vcard())
Ejemplo n.º 14
0
def main():
    # create the args parser
    parser = argparse.ArgumentParser(
        description="Khard is a carddav address book for the console",
        formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument(
        "-a",
        "--addressbook",
        default="",
        help="Specify address book names as comma separated list")
    parser.add_argument("-r",
                        "--reverse",
                        action="store_true",
                        help="Sort contacts in reverse order")
    parser.add_argument("-s", "--search", default="",
            help="Search in all contact data fields\n" \
                    "    default:   -s \"contact\"\n" \
                    "    merge:     -s \"source contact,target contact\"\n" \
                    "    copy/move: -s \"source contact,target address book\"")
    parser.add_argument(
        "-t",
        "--sort",
        default="alphabetical",
        help="Sort contacts list. Possible values: alphabetical, addressbook")
    parser.add_argument("-v",
                        "--version",
                        action="store_true",
                        help="Get current program version")
    parser.add_argument("action", nargs="?", default="",
            help="Possible actions:\n" \
                    "    list, details, source, mutt, alot, phone,\n" \
                    "    new, add-email, merge, modify, copy, move and remove")
    args = parser.parse_args()

    # version
    if args.version == True:
        print("Khard version %s" % khard_version)
        sys.exit(0)

    # validate value for action
    if args.action == "":
        args.action = Config().get_default_action()
    if args.action not in Config().get_list_of_actions():
        print("Unsupported action. Possible values are: %s" %
              ', '.join(Config().get_list_of_actions()))
        sys.exit(1)

    # load address books which are defined in the configuration file
    selected_address_books = []
    if args.addressbook == "":
        selected_address_books = Config().get_all_address_books()
    else:
        for name in args.addressbook.split(","):
            if Config().get_address_book(name) is None:
                print("Error: The entered address book \"%s\" does not exist.\nPossible values are: %s" \
                        % (name, ', '.join([ str(book) for book in Config().get_all_address_books() ])))
                sys.exit(1)
            else:
                selected_address_books.append(Config().get_address_book(name))

    # search parameter
    # may either contain one search term for a standard search or two terms, devided by a "," to
    # search for two contacts to merge them
    search_terms = args.search.split(",")
    if len(search_terms) == 1:
        search_terms.append("")

    # sort criteria
    if args.sort not in ["alphabetical", "addressbook"]:
        print(
            "Unsupported sort criteria. Possible values: alphabetical, addressbook"
        )
        sys.exit(1)

    # create a list of all found vcard objects
    vcard_list = get_contact_list_by_user_selection(selected_address_books,
                                                    args.sort, args.reverse,
                                                    search_terms[0], False)

    # create new contact
    if args.action == "new":
        if len(selected_address_books) != 1:
            if args.addressbook == "":
                print("Error: You must specify an address book for the new contact\nPossible values are: %s" \
                        % ', '.join([ str(book) for book in Config().get_all_address_books() ]))
            else:
                print("Please enter only one address book name")
            sys.exit(1)
        create_new_contact(selected_address_books[0])

    # add email address to contact or create a new one if necessary
    if args.action == "add-email":
        # get name and email address
        email_address = ""
        name = ""
        for line in sys.stdin:
            if line.startswith("From:"):
                try:
                    name = line[6:line.index("<") - 1]
                    email_address = line[line.index("<") + 1:line.index(">")]
                except ValueError as e:
                    email_address = line[6:].strip()
                break
        # reopen stdin to get user input
        sys.stdin = open('/dev/tty')
        print("Khard: Add email address to contact")
        if not email_address:
            print("Found no email address")
            sys.exit(1)
        print("Email address: %s" % email_address)
        if not name:
            name = raw_input("Contact's name: ")
        else:
            # remove quotes from name string, otherwise decoding fails
            name = name.replace("\"", "")
            # fix encoding of senders name
            name, encoding = decode_header(name)[0]
            if encoding:
                name = name.decode(encoding).encode("utf-8").replace("\"", "")
            # query user input.
            user_input = raw_input("Contact's name [%s]: " % name)
            # if empty, use the extracted name from above
            name = user_input or name

        # search for an existing contact
        selected_vcard = choose_vcard_from_list(
            get_contact_list_by_user_selection(selected_address_books,
                                               args.sort, args.reverse, name,
                                               True))
        if selected_vcard is None:
            # create new contact
            while True:
                input_string = raw_input(
                    "Contact %s does not exist. Do you want to create it (y/n)? "
                    % name)
                if input_string.lower() in ["", "n", "q"]:
                    print("Canceled")
                    sys.exit(0)
                if input_string.lower() == "y":
                    break
            # ask for address book, in which to create the new contact
            print("Available address books: %s" \
                    % ', '.join([ str(book) for book in Config().get_all_address_books() ]))
            while True:
                book_name = raw_input("Address book [%s]: " % selected_address_books[0].get_name()) \
                        or selected_address_books[0].get_name()
                if Config().get_address_book(book_name) is not None:
                    break
            # ask for name and organisation of new contact
            while True:
                first_name = raw_input("First name: ")
                last_name = raw_input("Last name: ")
                organization = raw_input("Organization: ")
                if not first_name and not last_name and not organization:
                    print("Error: All fields are empty.")
                else:
                    break
            selected_vcard = CarddavObject.new_contact(
                Config().get_address_book(book_name))
            selected_vcard.set_name_and_organisation(
                first_name.decode("utf-8"), last_name.decode("utf-8"),
                organization.decode("utf-8"))

        # check if the contact already contains the email address
        for email_entry in selected_vcard.get_email_addresses():
            if email_entry['value'] == email_address:
                print("The contact %s already contains the email address %s" %
                      (selected_vcard, email_address))
                sys.exit(0)

        # ask for confirmation again
        while True:
            input_string = raw_input("Do you want to add the email address %s to the contact %s (y/n)? " \
                    % (email_address, selected_vcard.get_full_name()))
            if input_string.lower() in ["", "n", "q"]:
                print("Canceled")
                sys.exit(0)
            if input_string.lower() == "y":
                break

        # ask for the email label
        print("\nAdding email address %s to contact %s" %
              (email_address, selected_vcard))
        label = raw_input("email label [home]: ") or "home"
        # add email address to vcard object
        selected_vcard.add_email_address(label.decode("utf-8"),
                                         email_address.decode("utf-8"))
        # save to disk
        selected_vcard.write_to_file(overwrite=True)
        print("Done.\n\n%s" % selected_vcard.print_vcard())

    # print phone application  friendly contacts table
    if args.action == "phone":
        phone_list = []
        regexp = re.compile(search_terms[0].replace(" ", ".*"), re.IGNORECASE)
        for vcard in vcard_list:
            for tel_entry in vcard.get_phone_numbers():
                phone_number_line = "%s\t%s\t%s" \
                        % (tel_entry['value'], vcard.get_full_name(), tel_entry['type'])
                if len(re.sub("\D", "", search_terms[0])) >= 3:
                    # the user likely searches for a phone number cause the search string contains
                    # at least three digits
                    # so we remove all non-digit chars from the phone number field and match against that
                    if regexp.search(re.sub("\D", "",
                                            tel_entry['value'])) != None:
                        phone_list.append(phone_number_line)
                else:
                    # the user doesn't search for a phone number so we can perform a standard search
                    # without removing all non-digit chars from the phone number string
                    if regexp.search(phone_number_line) != None:
                        phone_list.append(phone_number_line)
        print('\n'.join(phone_list))
        if len(phone_list) == 0:
            sys.exit(1)

    # print mutt friendly contacts table
    if args.action == "mutt":
        address_list = ["searching for '%s' ..." % search_terms[0]]
        regexp = re.compile(search_terms[0].replace(" ", ".*"), re.IGNORECASE)
        for vcard in vcard_list:
            for email_entry in vcard.get_email_addresses():
                email_address_line = "%s\t%s\t%s" \
                        % (email_entry['value'], vcard.get_full_name(), email_entry['type'])
                if regexp.search(email_address_line) != None:
                    address_list.append(email_address_line)
        print('\n'.join(address_list))
        if len(address_list) <= 1:
            sys.exit(1)

    # print alot friendly contacts table
    if args.action == "alot":
        address_list = []
        regexp = re.compile(search_terms[0].replace(" ", ".*"), re.IGNORECASE)
        for vcard in vcard_list:
            for email_entry in vcard.get_email_addresses():
                email_address_line = "\"%s %s\" <%s>" \
                        % (vcard.get_full_name(), email_entry['type'], email_entry['value'])
                if regexp.search(email_address_line) != None:
                    address_list.append(email_address_line)
        print('\n'.join(address_list))
        if len(address_list) == 0:
            sys.exit(1)

    # print user friendly contacts table
    if args.action == "list":
        if len(vcard_list) == 0:
            print("Found no contacts")
            sys.exit(1)
        list_contacts(vcard_list)

    # show source or details, modify or remove contact
    if args.action in ["details", "modify", "remove", "source"]:
        selected_vcard = choose_vcard_from_list(vcard_list)
        if selected_vcard is None:
            print("Found no contact")
            sys.exit(1)

        if args.action == "details":
            print selected_vcard.print_vcard()

        elif args.action == "modify":
            modify_existing_contact(selected_vcard)

        elif args.action == "remove":
            while True:
                input_string = raw_input("Deleting contact %s from address book %s. Are you sure? (y/n): " \
                        % (selected_vcard.get_full_name(), selected_vcard.get_address_book().get_name()))
                if input_string.lower() in ["", "n", "q"]:
                    print("Canceled")
                    sys.exit(0)
                if input_string.lower() == "y":
                    break
            selected_vcard.delete_vcard_file()
            print("Contact deleted successfully")

        elif args.action == "source":
            child = subprocess.Popen(
                [Config().get_editor(),
                 selected_vcard.get_filename()])
            streamdata = child.communicate()[0]

    # merge contacts
    if args.action == "merge":
        # get the source vcard, from which to merge
        source_vcard = choose_vcard_from_list(vcard_list)
        if source_vcard is None:
            print("Found no source contact for merging")
            sys.exit(1)

        # get the target vcard, into which to merge
        print("Merge from %s from address book %s\n\nNow choose the contact into which to merge:" \
                % (source_vcard.get_full_name(), source_vcard.get_address_book().get_name()))
        target_vcard = choose_vcard_from_list(
            get_contact_list_by_user_selection(selected_address_books,
                                               args.sort, args.reverse,
                                               search_terms[1], False))
        if target_vcard is None:
            print("Found no target contact for merging")
            sys.exit(1)

        # merging
        if source_vcard == target_vcard:
            print("The selected contacts are already identical")
        else:
            merge_existing_contacts(source_vcard, target_vcard, True)

    # copy or move contact
    if args.action in ["copy", "move"]:
        # get the source vcard, which to copy or move
        source_vcard = choose_vcard_from_list(vcard_list)
        if source_vcard is None:
            print("Found no contact")
            sys.exit(1)

        # get target address book from search query if provided
        available_address_books = [
            book for book in Config().get_all_address_books()
            if book != source_vcard.get_address_book()
        ]
        target_address_book = None
        if search_terms[1] != "":
            target_address_book = Config().get_address_book(search_terms[1])
            if target_address_book == None:
                print("The given target address book %s does not exist\n" %
                      search_terms[1])
            elif target_address_book not in available_address_books:
                print("The contact %s is already in the address book %s" \
                        % (source_vcard.get_full_name(), target_address_book.get_name()))
                sys.exit(1)

        # otherwise query the target address book name from user
        if target_address_book == None:
            print(
                "%s contact %s from address book %s\n\nAvailable address books:\n  %s"
                % (args.action.title(), source_vcard.get_full_name(),
                   source_vcard.get_address_book().get_name(), '\n  '.join(
                       [str(book) for book in available_address_books])))
        while target_address_book is None:
            input_string = raw_input("Into address book: ")
            if input_string == "":
                print("Canceled")
                sys.exit(0)
            if Config().get_address_book(
                    input_string) in available_address_books:
                print("")
                target_address_book = Config().get_address_book(input_string)

        # check if a contact already exists in the target address book
        target_vcard = choose_vcard_from_list(
            get_contact_list_by_user_selection([target_address_book],
                                               args.sort, args.reverse,
                                               source_vcard.get_full_name(),
                                               True))

        # if the target contact doesn't exist, move or copy the source contact into the target
        # address book without further questions
        if target_vcard is None:
            copy_contact(source_vcard, target_address_book,
                         args.action == "move")
        else:
            if source_vcard == target_vcard:
                # source and target contact are identical
                if args.action == "move":
                    copy_contact(source_vcard, target_address_book, True)
                else:
                    print("The selected contacts are already identical")
            else:
                # source and target contacts are different
                # either overwrite the target one or merge into target contact
                print("The address book %s already contains the contact %s\n\n" \
                        "Source\n\n%s\n\nTarget\n\n%s\n\n" \
                        "Possible actions:\n" \
                        "  o: Overwrite target contact\n  m: merge from source into target contact\n  q: quit" \
                        % (target_vcard.get_address_book().get_name(), source_vcard.get_full_name(),
                            source_vcard.print_vcard(), target_vcard.print_vcard()))
                while True:
                    input_string = raw_input("Your choice: ")
                    if input_string.lower() == "o":
                        copy_contact(source_vcard, target_address_book,
                                     args.action == "move")
                        break
                    if input_string.lower() == "m":
                        merge_existing_contacts(source_vcard, target_vcard,
                                                args.action == "move")
                        break
                    if input_string.lower() in ["", "q"]:
                        print("Canceled")
                        break
Ejemplo n.º 15
0
def merge_existing_contacts(source_contact, target_contact, delete_source_contact):
    # create temp files for each vcard
    # source vcard
    source_tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    source_temp_file_name = source_tf.name
    source_tf.write("# merge from %s\n%s" \
            % (source_contact.get_full_name(), source_contact.get_template()))
    source_tf.close()

    # target vcard
    target_tf = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
    target_temp_file_name = target_tf.name
    target_tf.write("# merge into %s\n%s" \
            % (target_contact.get_full_name(), target_contact.get_template()))
    target_tf.close()

    target_temp_file_creation = helpers.file_modification_date(target_temp_file_name)
    while True:
        # start editor to edit contact template
        child = subprocess.Popen([Config().get_merge_editor(), source_temp_file_name, target_temp_file_name])
        streamdata = child.communicate()[0]
        if target_temp_file_creation == helpers.file_modification_date(target_temp_file_name):
            merged_contact = None
            os.remove(source_temp_file_name)
            os.remove(target_temp_file_name)
            break

        # load target template contents
        target_tf = open(target_temp_file_name, "r")
        merged_contact_template = target_tf.read()
        target_tf.close()

        # try to create contact from user input
        try:
            merged_contact = CarddavObject.from_existing_contact_with_new_user_input(
                    target_contact, merged_contact_template)
        except ValueError as e:
            print("\n%s\n" % e)
            while True:
                input_string = raw_input("Do you want to open the editor again (y/n)? ")
                if input_string.lower() in ["", "n", "q"]:
                    print("Canceled")
                    os.remove(source_temp_file_name)
                    os.remove(target_temp_file_name)
                    sys.exit(0)
                if input_string.lower() == "y":
                    break
        else:
            os.remove(source_temp_file_name)
            os.remove(target_temp_file_name)
            break

    # compare them
    if merged_contact is None \
            or target_contact == merged_contact:
        print("Target contact unmodified\n\n%s" % target_contact.print_vcard())
        sys.exit(0)

    while True:
        if delete_source_contact:
            input_string = raw_input(
                    "Merge contact %s from address book %s into contact %s from address book %s\n\n" \
                        "To be removed\n\n%s\n\nMerged\n\n%s\n\nAre you sure? (y/n): " \
                    % (source_contact.get_full_name(), source_contact.get_address_book().get_name(),
                        merged_contact.get_full_name(), merged_contact.get_address_book().get_name(),
                        source_contact.print_vcard(), merged_contact.print_vcard()))
        else:
            input_string = raw_input(
                    "Merge contact %s from address book %s into contact %s from address book %s\n\n" \
                        "Keep unchanged\n\n%s\n\nMerged:\n\n%s\n\nAre you sure? (y/n): " \
                    % (source_contact.get_full_name(), source_contact.get_address_book().get_name(),
                        merged_contact.get_full_name(), merged_contact.get_address_book().get_name(),
                        source_contact.print_vcard(), merged_contact.print_vcard()))
        if input_string.lower() in ["", "n", "q"]:
            print("Canceled")
            return
        if input_string.lower() == "y":
            break

    # save merged_contact to disk and delete source contact
    merged_contact.write_to_file(overwrite=True)
    if delete_source_contact:
        source_contact.delete_vcard_file()
    print("Merge successful\n\n%s" % merged_contact.print_vcard())
Ejemplo n.º 16
0
        def __init__(self):
            # load config file
            config_file = os.path.join(os.path.expanduser("~"), ".config",
                                       "khard", "khard.conf")
            if os.path.exists(config_file) == False:
                print "Config file %s not available" % config_file
                sys.exit(2)
            self.config = ConfigObj(config_file, interpolation=False)

            # general settings
            if self.config.has_key("general") == False:
                print "Error in config file\nMissing main section \"[general]\"."
                sys.exit(2)
            if self.config['general'].has_key("editor") == False:
                print "Error in config file\nMissing editor parameter. Example: editor = /usr/bin/vim."
                sys.exit(2)
            elif os.path.exists(self.config['general']['editor']) == False:
                print "Error in config file\nInvalid editor path."
                sys.exit(2)
            if self.config['general'].has_key("default_country") == False:
                print "Error in config file\nMissing default country parameter."
                sys.exit(2)
            if self.config['general'].has_key("default_action") == False:
                print "Error in config file\nMissing default action parameter."
                sys.exit(2)
            elif self.config['general']['default_action'] not in [
                    "list", "details", "new", "modify", "remove"
            ]:
                print "Error in config file\nNon existing value for default action parameter. \
                        Possible values are: list, details, new, modify and remove"

                sys.exit(2)

            # load address books
            if self.config.has_key("addressbooks") == False:
                print "Error in config file\nMissing main section \"[addressbooks]\"."
                sys.exit(2)
            if self.config['addressbooks'].keys().__len__() == 0:
                print "Error in config file\nNo address book entries available."
                sys.exit(2)
            for name in self.config['addressbooks'].keys():
                addressbook = self.config['addressbooks'][name]
                if addressbook.has_key("path") == False:
                    print "Error in config file\nMissing path to the \"%s\" address book." % name
                    sys.exit(2)
                if addressbook['path'].startswith("~"):
                    addressbook['path'] = addressbook['path'].replace(
                        "~", os.path.expanduser("~"))
                if os.path.exists(addressbook['path']) == False:
                    print "Error in config file\nThe path %s to the address book %s does not exist." % (
                        addressbook['path'], name)
                    sys.exit(2)
                # set address book name
                addressbook['name'] = name
                # load all vcard files
                addressbook['vcards'] = []
                for filename in glob.glob(
                        os.path.join(addressbook['path'], "*.vcf")):
                    addressbook['vcards'].append(
                        CarddavObject(addressbook['name'], addressbook['path'],
                                      filename))
Ejemplo n.º 17
0
        def __init__(self):
            self.config = None
            self.address_book_list = []

            # load config file
            xdg_config_home = os.environ.get("XDG_CONFIG_HOME") or \
                    os.path.expanduser("~/.config")
            config_file = os.environ.get("KHARD_CONFIG") or \
                    os.path.join(xdg_config_home, "khard", "khard.conf")
            if os.path.exists(config_file) == False:
                print("Config file %s not available" % config_file)
                sys.exit(2)
            self.config = ConfigObj(config_file, interpolation=False)

            # general settings
            if self.config.has_key("general") == False:
                print(
                    "Error in config file\nMissing main section \"[general]\"."
                )
                sys.exit(2)

            # editor
            self.config['general']['editor'] = self.config['general'].get("editor") \
                    or os.environ.get("EDITOR")
            if self.config['general']['editor'] is None:
                print("Error in config file\n" \
                        "Set path to your preferred text editor in khard's config file or the $EDITOR shell variable\n" \
                        "Example for khard.conf: editor = vim")
                sys.exit(2)
            self.config['general']['editor'] = find_executable(
                os.path.expanduser(self.config['general']['editor']))
            if self.config['general']['editor'] is None:
                print(
                    "Error in config file\nInvalid editor path or executable not found."
                )
                sys.exit(2)

            # merge editor
            self.config['general']['merge_editor'] = self.config['general'].get("merge_editor") \
                    or os.environ.get("MERGE_EDITOR")
            if self.config['general']['merge_editor'] is None:
                print("Error in config file\n" \
                        "Set path to your preferred text merge editor in khard's config file or the $MERGE_EDITOR shell variable\n" \
                        "Example for khard.conf: merge_editor = vimdiff")
                sys.exit(2)
            self.config['general']['merge_editor'] = find_executable(
                os.path.expanduser(self.config['general']['merge_editor']))
            if self.config['general']['merge_editor'] is None:
                print(
                    "Error in config file\nInvalid merge editor path or executable not found."
                )
                sys.exit(2)

            # default values for action and nickname settings
            if self.config['general'].has_key("default_action") == False:
                print(
                    "Error in config file\nMissing default action parameter.")
                sys.exit(2)
            elif self.config['general'][
                    'default_action'] not in self.get_list_of_actions():
                print("Error in config file\n" \
                        "Non existing value for default action parameter\n" \
                        "Possible values are: %s" % ', '.join(self.get_list_of_actions()))
                sys.exit(2)
            if self.config['general'].has_key("show_nicknames") == False:
                self.config['general']['show_nicknames'] = False
            elif self.config['general']['show_nicknames'] == "yes":
                self.config['general']['show_nicknames'] = True
            elif self.config['general']['show_nicknames'] == "no":
                self.config['general']['show_nicknames'] = False
            else:
                print(
                    "Error in config file\nshow_nicknames parameter must be yes or no."
                )
                sys.exit(2)

            # load address books
            error_counter = 0
            if self.config.has_key("addressbooks") == False:
                print(
                    "Error in config file\nMissing main section \"[addressbooks]\"."
                )
                sys.exit(2)
            if len(self.config['addressbooks'].keys()) == 0:
                print(
                    "Error in config file\nNo address book entries available.")
                sys.exit(2)
            for name in self.config['addressbooks'].keys():
                # create address book object
                try:
                    address_book = AddressBook(
                        name, self.config['addressbooks'][name]['path'])
                except KeyError as e:
                    print(
                        "Error in config file\nMissing path to the \"%s\" address book."
                        % name)
                    sys.exit(2)
                except IOError as e:
                    print("Error in config file\n%s" % e)
                    sys.exit(2)

                # load all vcard files
                for filename in glob.glob(
                        os.path.join(address_book.get_path(), "*.vcf")):
                    try:
                        address_book.add_contact(
                            CarddavObject.from_file(address_book, filename))
                    except IOError as e:
                        print("Error: Could not open file %s\n%s" %
                              (filename, e))
                        error_counter += 1
                    except vobject.base.ParseError as e:
                        print("Error: Could not parse file %s\n%s" %
                              (filename, e))
                        error_counter += 1

                # add address book to list
                self.address_book_list.append(address_book)

            # check if one or more contacts could not be parsed
            if error_counter > 0:
                if error_counter == 1:
                    print("\n1 vcard file could not be parsed")
                elif error_counter > 1:
                    print("\n%d vcard files could not be parsed" %
                          error_counter)
                sys.exit(2)
Ejemplo n.º 18
0
        def __init__(self):
            self.config = None
            self.address_book_list = []
            self.uid_dict = {}

            # load config file
            xdg_config_home = os.environ.get("XDG_CONFIG_HOME") or \
                    os.path.expanduser("~/.config")
            config_file = os.environ.get("KHARD_CONFIG") or \
                    os.path.join(xdg_config_home, "khard", "khard.conf")
            if os.path.exists(config_file) == False:
                print("Config file %s not available" % config_file)
                sys.exit(2)
            self.config = ConfigObj(config_file, interpolation=False)

            # general settings
            if self.config.has_key("general") == False:
                print("Error in config file\nMissing main section \"[general]\".")
                sys.exit(2)

            # editor
            self.config['general']['editor'] = self.config['general'].get("editor") \
                    or os.environ.get("EDITOR")
            if self.config['general']['editor'] is None:
                print("Error in config file\n" \
                        "Set path to your preferred text editor in khard's config file or the $EDITOR shell variable\n" \
                        "Example for khard.conf: editor = vim")
                sys.exit(2)
            self.config['general']['editor'] = find_executable(
                    os.path.expanduser(self.config['general']['editor']))
            if self.config['general']['editor'] is None:
                print("Error in config file\nInvalid editor path or executable not found.")
                sys.exit(2)

            # merge editor
            self.config['general']['merge_editor'] = self.config['general'].get("merge_editor") \
                    or os.environ.get("MERGE_EDITOR")
            if self.config['general']['merge_editor'] is None:
                print("Error in config file\n" \
                        "Set path to your preferred text merge editor in khard's config file or the $MERGE_EDITOR shell variable\n" \
                        "Example for khard.conf: merge_editor = vimdiff")
                sys.exit(2)
            self.config['general']['merge_editor'] = find_executable(
                    os.path.expanduser(self.config['general']['merge_editor']))
            if self.config['general']['merge_editor'] is None:
                print("Error in config file\nInvalid merge editor path or executable not found.")
                sys.exit(2)

            # default action
            if self.config['general'].has_key("default_action") == False:
                print("Error in config file\nMissing default action parameter.")
                sys.exit(2)
            elif self.config['general']['default_action'] not in self.get_list_of_actions():
                print("Error in config file\n" \
                        "Invalid value for default_action parameter\n" \
                        "Possible values: %s" % ', '.join(self.get_list_of_actions()))
                sys.exit(2)

            # contact table settings
            if self.config.has_key("contact table") == False:
                self.config['contact table'] = {}

            # sort contacts table by first or last name
            if self.config['contact table'].has_key("sort") == False:
                self.config['contact table']['sort'] = "first_name"
            elif self.config['contact table']['sort'] not in ["first_name", "last_name"]:
                print("Error in config file\n" \
                        "Invalid value for sort parameter\n" \
                        "Possible values: first_name, last_name")
                sys.exit(2)

            # reverse contact table
            if self.config['contact table'].has_key("reverse") == False:
                self.config['contact table']['reverse'] = False
            elif self.config['contact table']['reverse'] == "yes":
                self.config['contact table']['reverse'] = True
            elif self.config['contact table']['reverse'] == "no":
                self.config['contact table']['reverse'] = False
            else:
                print("Error in config file\n" \
                        "Invalid value for reverse parameter\n" \
                        "Possible values: yes, no")
                sys.exit(2)

            # group contact table by address book
            if self.config['contact table'].has_key("group_by_addressbook") == False:
                self.config['contact table']['group_by_addressbook'] = False
            elif self.config['contact table']['group_by_addressbook'] == "yes":
                self.config['contact table']['group_by_addressbook'] = True
            elif self.config['contact table']['group_by_addressbook'] == "no":
                self.config['contact table']['group_by_addressbook'] = False
            else:
                print("Error in config file\n" \
                        "Invalid value for group_by_addressbook parameter\n" \
                        "Possible values: yes, no")
                sys.exit(2)

            # nickname
            if self.config['contact table'].has_key("show_nicknames") == False:
                self.config['contact table']['show_nicknames'] = False
            elif self.config['contact table']['show_nicknames'] == "yes":
                self.config['contact table']['show_nicknames'] = True
            elif self.config['contact table']['show_nicknames'] == "no":
                self.config['contact table']['show_nicknames'] = False
            else:
                print("Error in config file\n" \
                        "Invalid value for show_nicknames parameter\n" \
                        "Possible values: yes, no")
                sys.exit(2)

            # show uids
            if self.config['contact table'].has_key("show_uids") == False:
                self.config['contact table']['show_uids'] = True
            elif self.config['contact table']['show_uids'] == "yes":
                self.config['contact table']['show_uids'] = True
            elif self.config['contact table']['show_uids'] == "no":
                self.config['contact table']['show_uids'] = False
            else:
                print("Error in config file\n" \
                        "Invalid value for show_uids parameter\n" \
                        "Possible values: yes, no")
                sys.exit(2)

            # load address books and contacts
            error_counter = 0
            number_of_contacts = 0
            if self.config.has_key("addressbooks") == False:
                print("Error in config file\nMissing main section \"[addressbooks]\".")
                sys.exit(2)
            if len(self.config['addressbooks'].keys()) == 0:
                print("Error in config file\nNo address book entries available.")
                sys.exit(2)
            for name in self.config['addressbooks'].keys():
                # create address book object
                try:
                    address_book = AddressBook(name, self.config['addressbooks'][name]['path'])
                except KeyError as e:
                    print("Error in config file\nMissing path to the \"%s\" address book." % name)
                    sys.exit(2)
                except IOError as e:
                    print("Error in config file\n%s" % e)
                    sys.exit(2)

                # load all vcard files
                for filename in glob.glob(os.path.join(address_book.get_path(), "*.vcf")):
                    try:
                        address_book.add_contact(
                                CarddavObject.from_file(address_book, filename))
                        number_of_contacts += 1
                    except IOError as e:
                        print("Error: Could not open file %s\n%s" % (filename, e))
                        error_counter += 1
                    except vobject.base.ParseError as e:
                        print("Error: Could not parse file %s\n%s" % (filename, e))
                        error_counter += 1

                # add address book to list
                self.address_book_list.append(address_book)

            # check if one or more contacts could not be parsed
            if error_counter > 0:
                print("\n%d of %d vcard files could not be parsed" % (error_counter, number_of_contacts))
                sys.exit(2)

            # check uniqueness of vcard uids and create short uid dictionary
            # that can be disabled with the show_uids option in the config file, if desired
            if self.config['contact table']['show_uids']:
                # check, if multiple contacts have the same uid
                flat_contact_list = []
                for address_book in self.address_book_list:
                    for contact in address_book.get_contact_list():
                        uid = contact.get_uid()
                        if bool(uid):
                            matching_contact = self.uid_dict.get(uid)
                            if matching_contact is None:
                                self.uid_dict[uid] = contact
                            else:
                                print("The contact %s from address book %s" \
                                        " and the contact %s from address book %s have the same uid %s" \
                                        % (matching_contact.get_full_name(),
                                            matching_contact.get_address_book().get_name(),
                                            contact.get_full_name(),
                                            contact.get_address_book().get_name(),
                                            contact.get_uid())
                                        )
                                sys.exit(2)
                            # add to flat contact list
                            flat_contact_list.append(contact)

                # now we can be sure, that all uid's are unique but we don't want to enter
                # the whole uid, if we choose a contact by the -u / --uid option
                # so clear previously filled uid_dict and recreate with the shortest possible uid, so
                # that it's still unique and easier to enter
                # with around 100 contacts that short id should not be longer then two or three characters
                self.uid_dict.clear()
                flat_contact_list.sort(key = lambda x: x.get_uid())
                if len(flat_contact_list) == 1:
                    current = flat_contact_list[0]
                    self.uid_dict[current.get_uid()[:1]] = current
                elif len(flat_contact_list) > 1:
                    # first list element
                    current = flat_contact_list[0]
                    next = flat_contact_list[1]
                    same = helpers.compare_uids(current.get_uid(), next.get_uid())
                    self.uid_dict[current.get_uid()[:same+1]] = current
                    # list elements 1 to len(flat_contact_list)-1
                    for index in range(1, len(flat_contact_list)-1):
                        prev = flat_contact_list[index-1]
                        current = flat_contact_list[index]
                        next = flat_contact_list[index+1]
                        same = max(
                                helpers.compare_uids(prev.get_uid(), current.get_uid()),
                                helpers.compare_uids(current.get_uid(), next.get_uid()))
                        self.uid_dict[current.get_uid()[:same+1]] = current
                    # last list element
                    prev = flat_contact_list[-2]
                    current = flat_contact_list[-1]
                    same = helpers.compare_uids(prev.get_uid(), current.get_uid())
                    self.uid_dict[current.get_uid()[:same+1]] = current