Purism

Purism

Beautiful, Secure, Privacy-Respecting Laptops, Tablets, PCs, and Phones
Purism

The Librem 5 uses GNOME Contacts to manage contacts, but it does not yet have a way to import contacts from files. I decided to fix this and create a simple application to import contacts from vcard files. This means you can now easily migrate your contacts from Android and iCloud to the Librem 5!

Getting Help from the Community

If you have technical problems in the Free Software world chances are others ran into it too and publicly discussed how to solve it. The Openmoko project worked on a similar problem and Ubuntu Touch users have also discussed how to sync contacts. While these aren’t complete solutions, there’s plenty of useful advice to build on.

Also, don’t forget to ask for help and feedback from others 🙂 Thank you to Jeremiah, Kyle, Richard and Mladen from the Purism team for your help on my project.

The application:

You will user the following applications on your Librem 5 to build the application:

  • syncevolution: command line tool to import contacts
  • yad: to build GTK UIs
  • libnotify-bin: for notifications
  • awk and sed for working with text

Install the missing dependencies:

sudo apt install yad libnotify-bin syncevolution

Then let’s create a file for the app and make it executable:

touch /home/purism/bin/contacts-importer.sh
chmod a+x /home/purism/bin/contacts-importer.sh

File Dialog and Notifications

The first thing we are going to add is the user interface to select a vcard file.

FILE=`yad --title "Import Contacts" --text="Select a VCF file to import" --file=`

Passing the –file= option to yad allows the user to select a file. The –title and –text set the window title and helper text.

If there’s an error, for example the user didn’t select a file, show an error message and exit the application.

if [ "$FILE" == "" ]; then
`yad --title "Import Contacts" --text="You did not selected any file"` exit 1
fi

If there’s no error, notify the user that the file is being imported:

notify-send -t 1000 "Import Contacts" "Importing Contacts from $FILE"

Storing Contacts

GNOME contacts uses Evolution Data Server as a backend and it can have several different contact databases. I’ve decided to import contacts into the default local system address book. To import the contacts we need to provide an address book name (“Personal”) but it changes with the system language. If PureOS is configured to use Portuguese it will be called “Pessoal”, in German “Persönlich” and so on.

To solve this we use syncevolution to list of all Evolution Data Server contacts and calendar databases, grep to filter the list and sed to clean the result and store the correct name in CONTACTDB.

CONTACTDB=$(syncevolution --print-databases | grep "system-address-book" | sed 's#(.*##' | sed 's# ##g' )

We use the file command to get the mimetype of the user’s file, and if it is not equal to text/vcard we display an error message and exit the application.

MIMETYPE=$(file --mime-type -b $FILE)
CONTROL="text/vcard"

if [[ "$MIMETYPE" != "$CONTROL" ]]; then
    notify-send -t 1000 "File is not a vcard! Bye!" && exit 1
fi

Workaround syncevolution Issue

Unfortunately syncevolution has an issue processing vcard files with multiple contacts not separated with new lines. To workaround this we will break a vcard into multipe vcard files each with a single contact.

It is good practice to create temporary files under the /tmp directory rather than the user’s HOME directory, you don’t want to risk modifying a user’s personal files!

TEMP=$(mktemp -d -t contacts-importer-XXXXXXXXXXXXX-$(date +%Y-%m-%d-%H-%M-%S))

In our temporary directory we use cat to read the vcard file $FILE and awk to search and split each vcard (beginning with “BEGIN:VCARD”) and save it to a file “card_import_L5_n” where n is the contact number.

cd $TEMP
cat $FILE | awk ‘ /BEGIN:VCARD/ { ++a; fn=sprintf(“card_import_L5_%02d.vcf”, a); print “Writing: “, fn } { print $0 » fn; } ‘ $1

Now all we have to do is loop over and import the multiple vcard files:

for f in $TEMP/card_import_L5*
do syncevolution --import ${f%} backend=evolution-contacts database=${CONTACTDB}
done

Final checks

To finish off, we check if the import was successful and notify the user.

if [ $? -eq 0 ] then
    notify-send "Successful import";
    exit 0
else
    notify-send "Error" >&2
    exit 1
fi

The Complete Application:

#!/bin/bash -e

# Original Script: https://www.isticktoit.net/?p=1536
# Adapted for the L5 use case with gnome-contacts

#Selecting a file

FILE=`yad --title "Import Contacts" --text="Select a VCF file to import" --file=`

if [ "$FILE" == "" ]; then
  `yad --title "Import Contacts" --text="You did not selected any file"` exit 1
fi

notify-send -t 1000 "Import Contacts" "Importing Contacts from $FILE"

#Name of the database where the contacts will be imported to.
CONTACTDB=$(syncevolution --print-databases | grep "system-address-book"| sed 's#(.*##' | sed 's# ##g' )

#Temp directory to mess with files:

TEMP=$(mktemp -d -t contacts-importer-XXXXXXXXXXXXX-$(date +%Y-%m-%d-%H-%M-%S))

#Some control info to make sure the file is a vcard

MIMETYPE=$(file --mime-type -b $FILE)
CONTROL="text/vcard"

if [[ "$MIMETYPE" != "$CONTROL" ]]; then
	notify-send -t 1000 "File is not a vcard! Bye!" && exit 1
fi

#Begin importing contacts

cd $TEMP

cat $FILE | awk ' /BEGIN:VCARD/ { ++a; fn=sprintf("card_import_L5_%02d.vcf", a); print "Writing: ", fn } { print $0 >> fn; } ' $1

for f in $TEMP/card_import_L5* 
	do syncevolution --import ${f%} backend=evolution-contacts database=${CONTACTDB} 
done

if [ $? -eq 0 ]
then
    notify-send "Successful import";
    exit 0
else
    notify-send "Error" >&2
    exit 1
fi

Make it Fancy

Following Kyle’s lead in his Flashlight App and Screenshot App tutorial let’s make our app easier to use by adding a Desktop Entry.

Create the Desktop Entry file:

touch /home/purism/.local/share/applications/contacts-importer.desktop

Then add the following content:

[Desktop Entry]
Name=Contacts Importer
Type=Application
Icon=preferences-desktop-personal
Exec=contacts-importer.sh
Categories=Utility;

Your app will now have convenient icon in the app drawer.

Demo Time

The Librem 5 runs pretty much the same software stack as regular PureOS which means our app will run on most GNU/Linux systems. Let’s see what it looks like on a Librem laptop running PureOS:

Conclusion

With only 25 lines of bash (and a desktop entry) we created a complete application with error handling and notifications that runs on desktop and mobile Linux. This is the power of the Librem 5, a fully-featured desktop GNU/Linux ecosystem in your pocket!

Discover the Librem 5

Purism believes building the Librem 5 is just one step on the road to launching a digital rights movement, where we—the-people stand up for our digital rights, where we place the control of your data and your family’s data back where it belongs: in your own hands.

Preorder now

Recent Posts

Related Content

Tags