#!/usr/bin/perl -w # Copyright © 2006 Jamie Zawinski # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation. No representations are made about the suitability of this # software for any purpose. It is provided "as is" without express or # implied warranty. # # Runs "sms2csv" and saves the output in multiple files: # one file per phone number messages have been sent to or received from, # per month. E.g., ~/Documents/SMS Backup/YYYY-MM.NNNNNNNNNN.txt # # It only changes files when needed, and will never delete a message # from one of the files (so if your phone's "Messages Database.pdb" file # has shrunk, your old archived messages won't be lost.) # # Get the "sms2csv" program from http://multik.org/sms2csv/ # # Note that sometimes the dates on messages in the database change at # random, causing old messages to be duplicated. I don't know if this # is a bug in sms2csv, or in PalmOS... # # Created: 21-Jun-2006. require 5; use diagnostics; use strict; use POSIX; my $progname = $0; $progname =~ s@.*/@@g; my $version = q{ $Revision: 1.6 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/; my $verbose = 0; my $debug_p = 0; my $sms_pdb_file = "$ENV{HOME}/Documents/Palm/Users/$ENV{USER}" . "/Backups/Messages Database.pdb"; my $output_dir = "$ENV{HOME}/Documents/SMS Backup"; $ENV{PATH} = "$ENV{HOME}/bin:$ENV{PATH}"; # for cron, bleh. sub csv_split($$) { my ($body, $orig_p) = @_; if ($orig_p) { $body =~ s/^([RS],)/\001$1/gm; } else { $body =~ s/^([<>] )/\001$1/gm; } my @lines = split (/\001/, $body); shift @lines; # lose first blank line return @lines; } sub sms_backup() { print STDERR "exec: sms2csv \"$sms_pdb_file\"\n" if ($verbose > 2); my $lines = `sms2csv "$sms_pdb_file"`; my @lines = csv_split ($lines, 1); my %output; print STDERR "" . ($#lines + 1) . " lines\n" if ($verbose > 2); my $lineno = 0; foreach (@lines) { $lineno++; my ($type, $time, $number, $name, $body) = m/^([^,]*),([^,]*),([^,]*),([^,]*),(.*)$/s; if (! $type) { print STDERR "$progname: bogus line $lineno\n"; next; } my ($dd, $mon, $yyyy, $hh, $mm, $ss) = ($time =~ m/^(\d\d)\.(\d\d)\.(\d\d\d\d) (\d\d):(\d\d):(\d\d)$/); $hh += 3; # I think sms2csv has an "off by three hours" bug... $time = mktime ($ss, $mm, $hh, $dd, $mon-1, $yyyy-1900); my $timestr = strftime ("%a %b %d %I:%m %p", localtime($time)); $type = '<' if ($type eq 'R'); $type = '>' if ($type eq 'S'); $body =~ s/\n([<>])/\n $1/gs; # strip hyphens/dots from phone numbers, for consistency $number =~ s/[-.]//gs if ($number =~ m/^[-.\d]+$/); $number = "unknown" if ($number eq ''); my $line = "$type $timestr $number $name \t$body"; my $month_str = strftime ("%Y-%m", localtime ($time)); my $filename = "$output_dir/$month_str.$number.txt"; $output{"$filename"} = ($output{"$filename"} || '') . $line; } foreach my $file (sort keys (%output)) { my $body = $output{$file}; write_changes ($file, $body); } } sub write_changes($$) { my ($file, $nbody) = @_; my $obody = ''; my %olines; my $count = 0; my $count2 = 0; local *IN; if (open (IN, "<$file")) { while () { $obody .= $_; } close IN; foreach my $line (csv_split ($obody, 0)) { $count++; $olines{$line} = 1; } } my @nlines = (); foreach my $line (csv_split ($nbody, 0)) { if (! $olines{$line}) { $count++; $count2++; push @nlines, $line; } } my ($year, $mon) = ($file =~ m@/(\d\d\d\d)-(\d\d)\.[^/]+$@); my @now = localtime(time); my $now = (($now[5] + 1900) * 10000 + $now[4]); my $then = ($year * 10000 + $mon); my $old_p = ($now - $then) > 2; if ($#nlines < 0) { if ($verbose > 1) { $file =~ s@^.*/@@; print STDERR "$progname: $file: unchanged\n"; } } else { local *OUT; if (! $debug_p || $old_p) { open (OUT, ">$file") || error ("$file: $!"); print OUT $obody; } foreach (@nlines) { if ($verbose > 2 && $obody ne '') { print STDERR "+ $_"; } print OUT $_ unless ($debug_p || $old_p); } close OUT unless ($debug_p || $old_p); if ($verbose) { $file =~ s@^.*/@@; print STDERR ("$progname: " . (($debug_p || $old_p) ? "didn't write" : "wrote") . ($old_p ? " old file" : "") . " $file ($count2 of $count lines)\n"); } } } sub error($) { my ($err) = @_; print STDERR "$progname: $err\n"; exit 1; } sub usage() { print STDERR "usage: $progname [--verbose] [--debug]\n"; exit 1; } sub main() { while ($#ARGV >= 0) { $_ = shift @ARGV; if ($_ eq "--verbose") { $verbose++; } elsif (m/^-v+$/) { $verbose += length($_)-1; } elsif ($_ eq "--debug") { $debug_p++; } elsif (m/^-./) { usage; } else { usage; } } $verbose += 3 if ($debug_p); sms_backup(); } main(); exit 0;