Skip to content

Creating iPhone/iPod/iPad notes from the shell

Open Source

I found a very nice script to create Notes on the iPhone from the command line by hossman over at Perlmonks.

For some weird reason Perlmonks does not allow me to reply with amendments even after I created an account. I can "preview" a reply at Perlmonks but after "create" I get "Permission Denied". Duh. vroom, if you want screenshots, contact me on IRC :-).

As I wrote everything up for the Perlmonks reply anyways, I'll post it here instead.

Against hossman's version 32 from 2011-02-22 I changed the following:

  • removed .pl from filename and documentation
  • added --list to list existing notes
  • added --hosteurope for Hosteurope mail account preferences and with it a sample how to add username and password into the script for unattended use
  • made the "Notes" folder the default (so -f Notes becomes obsolete)
  • added some UTF-8 conversions to make Umlauts work better (this is a mess in perl, see Jeremy Zawodny's writeup and Ivan Kurmanov's blog entry for some further solutions). Please try combinations of utf8::encode and ::decode, binmode utf8 for STDIN and/or STDOUT and the other hints from these linked blog entries in your local setup to get Umlauts and other non-7bit ASCII characters working. Be patient. There's more than one way to do it :-).

I /msg'd hossman the URL of this blog entry.

#!/usr/bin/perl

#
# run iphonenote --help for usage
#
# Source http://www.perlmonks.org/?node_id=851322
# This version at http://daniel-lange.com/archives/64-Creating-iPhoneiPodiPad-notes-from-the-shell.html
# DL120201 changed for local use :-)
# DL120204 added --list, utf8 handling
#
# $Id: iphonenote.pl 32 2011-02-22 04:26:00Z hossman $
#

use strict;
use warnings;

use Getopt::Long;
use Pod::Usage;

use Data::UUID;

use Mail::Message;
use Mail::Box::Manager;

use constant SUBJ_HEADER => 'Subject';
use constant UUID_HEADER => 'X-Universally-Unique-Identifier';
use constant TYPE_HEADER => 'X-Uniform-Type-Identifier';
use constant TYPE_VALUE  => 'com.apple.mail-note';

my %folder_opts = ('access'=>'rw');
my $foldername = "Notes";
my $msg = undef;
my $uuid = undef;
my $help = 0;
my $gmail = 0;
my $hosteurope = 0;
my $create = 0;
my $modify = 0;
my $delete = 0;
my $list = 0;

#binmode STDIN, ":utf8";
#binmode STDOUT, ":utf8";

sub trim($)
{
        my $string = shift;
        $string =~ s/^\s+//;
        $string =~ s/\s+$//;
        $string =~ s/\t/ /g;
        utf8::decode($string);
        return $string;
}

sub numformat($)
{
        my $string = shift;
        $string =~ s/(^[-+]?\d+?(?=(?>(?:\d{3})+)(?!\d))|\G\d{3}(?=\d))/$1\./g;
        return $string;
}

GetOptions('help|usage|?' => \$help,
           'folder|f=s'   => \$foldername,
           'opt|opts=s%'  => \%folder_opts,
           'uuid|u=s'     => \$uuid,
           'create|c'     => \$create,
           'modify|m'     => \$modify,
           'delete|d'     => \$delete,
           'list|l'       => \$list,
           'gmail|g'      => \$gmail,
           'hosteurope|he' => \$hosteurope,
    ) or pod2usage(2);

pod2usage(1) if $help;

if ($gmail) {
    $foldername = 'Notes';
    $folder_opts{'server_name'} = 'imap.gmail.com';
    $folder_opts{'type'} = 'imaps';
}

if ($hosteurope) {
    $foldername = 'Notes';
    $folder_opts{'server_name'} = 'yourserver.hosteurope.de';
    $folder_opts{'type'} = 'imaps';
    $folder_opts{'username'} = 'the_username_goes_here' unless defined $folder_opts{'username'};
    $folder_opts{'password'} = 's3cr3t' unless defined $folder_opts{'password'};
}

pod2usage("Can not use more then one of --list, --create, --modify, --delete")
    if (1 < $list + $create + $modify + $delete);
pod2usage("Must specify one of --list, --create, --modify, --delete")
    if (0 == $list + $create + $modify + $delete);
pod2usage("must specify a --uuid to use --modify or --delete")
    unless (($create or $list) or defined $uuid);

my $mgr = Mail::Box::Manager->new;

# really wish Mail::Box impls registered themselves by default
if (exists $folder_opts{'type'} and $folder_opts{'type'} eq 'imaps') {
    $mgr->registerType('imaps' => 'Mail::Box::IMAP4::SSL');
}

my $folder = $mgr->open($foldername, %folder_opts)
    or die "Can't open folder: $foldername\n";

if ($list) {
foreach $msg ($folder->messages) { # all messages
    printf "%-40s %-36s %-40s [%11s bytes]\n", $msg->head->get(UUID_HEADER()), $msg->head->get('Date'), trim($msg->subject), numformat($msg->size());
    }
}

if ($modify or $delete) {
    # find the existing message

    # Ugh, gmail doesn't seem to support whatever Mail::Box::Search::Grep
    # tries to do to find messages with a specific header name=val
    my @matches =
        grep { $_->head->get(UUID_HEADER()) eq $uuid } $folder->messages();

    my $num = scalar @matches;
    if (1 < $num) {
        die "Found too many messages: $num";
    } elsif ($num < 1) {
        die "Didn't find any matches";
    }
   
    # delete now, add a new one later
    $matches[0]->delete();
}


if ($create or $modify) {
    # create the "new" message

    $uuid = Data::UUID->new()->create_str()
        unless defined $uuid;
    my @lines = <STDIN>;
# depending on your local utf8 brokenness you may want to uncomment the following lines if you work with non-English characters
#    foreach (@lines) {
#       utf8::encode($_);
#    }
    my $subject = $lines[0];
    chomp $subject;
    utf8::encode($subject);

    my $msg = Mail::Message->build
        ( UUID_HEADER() => $uuid,
          TYPE_HEADER() => TYPE_VALUE,
          SUBJ_HEADER() => $subject,
          'data' => \@lines,
        );
    $mgr->appendMessage($folder, $msg);
    $folder->close();
    $mgr->close();
}
 
__END__

=head1 NAME

iphonenote - Manipulate iPhone notes in a mail folder

=head1 SYNOPSIS

 iphonenote -c [-u uid] < somefile.txt
 iphonenote -m -u uid   < somefile.txt
 iphonenote -d -u uid
 iphonenote --gmail --opts username=foo@gmail.com --opts password=s3cr3t ...

 Options:
  -h  or --help          Print this help documentation
  -f  or --folder        The Mail::Box::Manager compliant foldername to manipulate
  -u  or --uuid          The UUID of the specific note to manipulate in that folder
  -l  or --list          To list existing notes
  -c  or --create        To create a new note
  -m  or --modify        To modify an existing note (requires --uuid)
  -d  or --delete        To delete an existing note (requires --uuid)
         --opts          key=val pairs of Mail::Box options
  -g  or --gmail         sets default --opts for using gmail
  -he or --hosteurope    sets default --opts for using hosteurope mailer

The script is available for download(5kB).
For Debian/Ubuntu you need to apt-get install libmail-box-perl libmail-imapclient-perl and libossp-uuid-perl (the latter for for Data/UUID.pm ... anything but obvious).
For /usr/share/perl5/Mail/Box/IMAP4/SSL.[pm|pod] you need to head over to CPAN for the Mail-Box-IMAP4-SSL-0.02 module. I did not find these files packaged in a .deb somewhere.

Trackbacks

No Trackbacks

Comments

Display comments as Linear | Threaded

jaffa on :

Great blog.

Add Comment

Markdown format allowed
Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

Form options

Submitted comments will be subject to moderation before being displayed.