|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package Palm::Treo680MessagesDB;  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
3
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
173014
 | 
 use strict;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
291
 | 
    | 
| 
4
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
34
 | 
 use warnings;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
245
 | 
    | 
| 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
6
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
3566
 | 
 use Palm::Raw();  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2544
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
147
 | 
    | 
| 
7
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
6469
 | 
 use DateTime;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
941141
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
322
 | 
    | 
| 
8
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
4730
 | 
 use Data::Hexdumper ();  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11474
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
227
 | 
    | 
| 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
10
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
47
 | 
 use vars qw($VERSION @ISA $timezone $incl_raw $debug $multipart);  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1302
 | 
    | 
| 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 $VERSION = '1.02';  | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 @ISA = qw(Palm::Raw);  | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 $timezone = 'Europe/London';  | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 $debug = 0;  | 
| 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 $incl_raw = 0;  | 
| 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 $multipart = {};  | 
| 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub import {  | 
| 
21
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
66
 | 
     my $class = shift;  | 
| 
22
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
     my %opts = @_;  | 
| 
23
 | 
8
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
34
 | 
     $timezone = $opts{timezone} if(exists($opts{timezone}));  | 
| 
24
 | 
8
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
19
 | 
     $incl_raw = $opts{incl_raw} if(exists($opts{incl_raw}));  | 
| 
25
 | 
8
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
27
 | 
     $debug    = $opts{debug}    if(exists($opts{debug}));  | 
| 
26
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
42
 | 
     Palm::PDB::RegisterPDBHandlers(__PACKAGE__, [MsSt => 'MsDb']);  | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
28
 | 
8
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1868
 | 
     if(!$debug) {  | 
| 
29
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
 
 | 
69
 | 
         no warnings;  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13
 | 
    | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13190
 | 
    | 
| 
30
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
         my $orig_Load = \&Palm::PDB::Load;  | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         *Palm::PDB::Load = sub {  | 
| 
32
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
 
 | 
657
 | 
             $orig_Load->(@_);  | 
| 
33
 | 
3594
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
16572
 | 
             $_[0]->{records} = [  | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 grep {  | 
| 
35
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
42
 | 
                     $_->{type} ne 'unknown' &&  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                     !(exists($_->{epoch}) && $_->{epoch} < 946684800) # 2000-01-01 00:00  | 
| 
37
 | 
6
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
2336
 | 
                 } @{$_[0]->{records}}  | 
| 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ] if(  | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $_[0]->{creator} eq 'MsSt' &&  | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $_[0]->{type}    eq 'MsDb'  | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             );  | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
43
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11652
 | 
     }  | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 NAME  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Palm::Treo680MessagesDB - Handler for Treo 680 SMS message databases  | 
| 
49
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 SYNOPSIS  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     use Palm::PDB;  | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     use Palm::Treo680MessagesDB timezone => 'Europe/London';  | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     use Data::Dumper;  | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $pdb = Palm::PDB->new();  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $pdb->Load("MessagesDB.pdb");  | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     print Dumper(@{$pdb->{records}});  | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 DESCRIPTION  | 
| 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
62
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This is a helper class for the Palm::PDB package, which parses the  | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 database generated by a Treo 680 as a record of all your SMSes.  | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 OPTIONS  | 
| 
66
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 You can set some global options when you 'use' the module:  | 
| 
68
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
69
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over  | 
| 
70
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
71
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item timezone  | 
| 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Defaults to 'Europe/London'.  | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item incl_raw  | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Whether to include the raw binary blob of data in the parsed records.  | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Defaults to false.  | 
| 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item debug  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Defaults to false.  | 
| 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 If false, unknown record-types and those which look like they weren't  | 
| 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 parsed properly (eg they have an impossible timestamp) are suppressed.  | 
| 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This is done by over-riding Palm::PDB's C method.  | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
88
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 If true, include a hexadecimal dump of each record in the 'debug'  | 
| 
89
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 field, and don't suppress unknown or badly parsed records.  | 
| 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
91
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
93
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 METHODS  | 
| 
94
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This class inherits from Palm::Raw, so has all of its methods.  The  | 
| 
96
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 folliwing are over-ridden, and differ from that in the parent class  | 
| 
97
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 thus:  | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head2 ParseRecord  | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Returns data structures with the following keys:  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =over  | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item rawdata  | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
107
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 The raw data blob passed to the method.  This is only present if the  | 
| 
108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 incl_raw option is true.  | 
| 
109
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item date  | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 The date of the message, if available, in YYYY-MM-DD format  | 
| 
113
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
114
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item time  | 
| 
115
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 The time of the message, if available, in HH:MM format  | 
| 
117
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item epoch or timestamp (it's available under both names)  | 
| 
119
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 The epoch time of the message, if available.  Note that because  | 
| 
121
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 the database doesn't  | 
| 
122
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 store the timezone, we assume 'Europe/London' when converting this  | 
| 
123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 to the seperate date and time fields.  If you want to change  | 
| 
124
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 that, then suppy a timezone option when you 'use' the module.  | 
| 
125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Note that this is always the Unix epoch time, even though PalmOS  | 
| 
127
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 uses an epoch based on 1904.  | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item name  | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
131
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 The name of the other party, which the Treo extracts from the SIM  | 
| 
132
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 phone-book or from the Palm address book at the time the SMS is saved.  | 
| 
133
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item number or phone  | 
| 
135
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
136
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 The number of the other party.  This is not normalised so you might see  | 
| 
137
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 the same number in different formats, eg 07979866975 and +447979866975.  | 
| 
138
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 I may add number normalisation in the future.  | 
| 
139
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
140
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =item direction  | 
| 
141
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
142
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Either 'incoming', or 'outgoing'.  | 
| 
143
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
144
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =back  | 
| 
145
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
146
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Other fields may be added in the future.  | 
| 
147
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
148
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
149
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
150
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub ParseRecord {  | 
| 
151
 | 
4193
 | 
 
 | 
 
 | 
  
4193
  
 | 
  
1
  
 | 
143291
 | 
     my $self = shift;  | 
| 
152
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12643
 | 
     my %record = @_;  | 
| 
153
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
154
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7024
 | 
     $record{rawdata} = delete($record{data});  | 
| 
155
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7091
 | 
     my $parsed = _parseblob($record{rawdata});  | 
| 
156
 | 
4193
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
10451
 | 
     delete $record{rawdata} unless($incl_raw);  | 
| 
157
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
158
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8192
 | 
     return {%record, %{$parsed}};  | 
| 
 
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35409
 | 
    | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub _parseblob {  | 
| 
162
 | 
4193
 | 
 
 | 
 
 | 
  
4193
  
 | 
 
 | 
3641
 | 
     my $buf = shift;  | 
| 
163
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4445
 | 
     my %record = ();  | 
| 
164
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
165
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8634
 | 
     my $type = 256 * ord(substr($buf, 10, 1)) + ord(substr($buf, 11, 1));  | 
| 
166
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5247
 | 
     my($dir, $num, $name, $msg) = ('', '', '', '');  | 
| 
167
 | 
4193
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
16493
 | 
     if($type == 0x400C || $type == 0x4009) { # 4009 not used by 680?  | 
| 
 
 | 
 
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
168
 | 
2247
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
3790
 | 
         $dir = ($type == 0x400C) ? 'inbound' : 'outbound';  | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # ASCIIZ number starting at 0x22  | 
| 
171
 | 
2247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13075
 | 
         ($num  = substr($buf, 0x22)) =~ s/\00.*//s;  | 
| 
172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
173
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # immediately followed by ASCIIZ name, with some trailing 0s  | 
| 
174
 | 
2247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4541
 | 
         $name = substr($buf, length($num) + 1 + 0x22);  | 
| 
175
 | 
2247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7945
 | 
         $name =~ /^([^\00]*?)\00+(.*)$/s;  | 
| 
176
 | 
2247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6674
 | 
         ($name, my $after_name) = ($1, $2);  | 
| 
177
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
178
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # four unknown bytes, then ASCIIZ message  | 
| 
179
 | 
2247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6846
 | 
         ($msg = substr($after_name, 4)) =~ s/\00.*//s;  | 
| 
180
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
181
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # two unknown bytes, then 32-bit time_t, but with 1904 epoch  | 
| 
182
 | 
2247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3851
 | 
         my $epoch = substr($after_name, 4 + length($msg) + 1 + 2, 4);  | 
| 
183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
184
 | 
2247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5960
 | 
         $record{epoch} =  | 
| 
185
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x1000000 * ord(substr($epoch, 0, 1)) +  | 
| 
186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x10000   * ord(substr($epoch, 1, 1)) +  | 
| 
187
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x100     * ord(substr($epoch, 2, 1)) +  | 
| 
188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                              ord(substr($epoch, 3, 1)) -  | 
| 
189
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  2082844800; # offset from Palm epoch (1904) to Unix  | 
| 
190
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
191
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # if is because DateTime::from_epoch seems to DTwrongT on Win32  | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # when you get a negative epoch  | 
| 
193
 | 
2247
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
4427
 | 
         if($record{epoch} > 0) {  | 
| 
194
 | 
2009
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6697
 | 
             my $dt = DateTime->from_epoch(  | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 epoch => $record{epoch},  | 
| 
196
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 time_zone => $timezone  | 
| 
197
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             );  | 
| 
198
 | 
2009
 | 
 
 | 
 
 | 
 
 | 
 
 | 
829924
 | 
             $record{date} = sprintf('%04d-%02d-%02d', $dt->year(), $dt->month(), $dt->day());  | 
| 
199
 | 
2009
 | 
 
 | 
 
 | 
 
 | 
 
 | 
24145
 | 
             $record{time} = sprintf('%02d:%02d', $dt->hour(), $dt->minute());  | 
| 
200
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif($type == 0x0002) {  | 
| 
202
 | 
1505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1714
 | 
         $dir = 'outbound';  | 
| 
203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # ASCIIZ number starting at 0x46  | 
| 
205
 | 
1505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8559
 | 
         ($num  = substr($buf, 0x46)) =~ s/\00.*//s;  | 
| 
206
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
207
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # immediately followed by ASCIIZ name, with some trailing 0s  | 
| 
208
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # some Trsm gibberish, then an ASCIIZ message  | 
| 
209
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # $name = substr($buf, length($num) + 1 + 0x46);  | 
| 
210
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # $name =~ /^([^\00]+)\00+.Trsm....([^\00]+)\00.*$/s;  | 
| 
211
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # ($name, $msg) = ($1, $2);  | 
| 
212
 | 
1505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5513
 | 
         ($name = substr($buf, length($num) + 1 + 0x46)) =~ s/\00.*//s;  | 
| 
213
 | 
1505
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
2864
 | 
         $name = undef unless(length($name));  | 
| 
214
 | 
1505
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
5295
 | 
         $name .= " (may be truncated)" if($name && length($name) == 31);  | 
| 
215
 | 
1505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9854
 | 
         ($msg = $buf) =~ s/^.*?Trsm....(([^\00]+)\00.*)$/$2/s;  | 
| 
216
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
217
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # 32-bit time_t, but with 1904 epoch  | 
| 
218
 | 
1505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2180
 | 
         my $epoch = substr($buf, 0x24, 4);  | 
| 
219
 | 
1505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4583
 | 
         $record{epoch} =  | 
| 
220
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x1000000 * ord(substr($epoch, 0, 1)) +  | 
| 
221
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x10000   * ord(substr($epoch, 1, 1)) +  | 
| 
222
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x100     * ord(substr($epoch, 2, 1)) +  | 
| 
223
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                              ord(substr($epoch, 3, 1)) -  | 
| 
224
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  2082844800;  | 
| 
225
 | 
1505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5031
 | 
         my $dt = DateTime->from_epoch(  | 
| 
226
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             epoch => $record{epoch},  | 
| 
227
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             time_zone => $timezone  | 
| 
228
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         );  | 
| 
229
 | 
1505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
626458
 | 
         $record{date} = sprintf('%04d-%02d-%02d', $dt->year(), $dt->month(), $dt->day());  | 
| 
230
 | 
1505
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18060
 | 
         $record{time} = sprintf('%02d:%02d', $dt->hour(), $dt->minute());  | 
| 
231
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
232
 | 
1505
 | 
  
100
  
 | 
  
 66
  
 | 
 
 | 
 
 | 
15528
 | 
         if($msg eq "\01N@" && length($1) == 14) { # no real body. bleh  | 
| 
233
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
25
 | 
             delete @record{qw(epoch date time)};  | 
| 
234
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
40
 | 
             $type = 'unknown';  | 
| 
235
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
236
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif($type == 0x0001) {  | 
| 
237
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
         $dir = 'outbound';  | 
| 
238
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
239
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # number field at 0x4C, possibly including some leading crap  | 
| 
240
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # then an ASCIIZ number  | 
| 
241
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
58
 | 
         ($num  = substr($buf, 0x4C)) =~ s/(^\00*[^\00]+)\00.*/$1/s;  | 
| 
242
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
243
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # immediately followed by ASCIIZ name, with some trailing 0s  | 
| 
244
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
48
 | 
         ($name = substr($buf, length($num) + 0x4C + 1)) =~ s/\00.*//s;  | 
| 
245
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # ASCIIZ message, prefixed by 0x20 0x02 16-bit length word  | 
| 
247
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
         $msg = substr($buf, length($num) + 0x4C + 1 + length($name) + 1);  | 
| 
248
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
73
 | 
         $msg =~ s/^.*\x20\x02..|\00.*$//g;  | 
| 
249
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           | 
| 
250
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
27
 | 
         $num =~ s/^[^0-9+]+//; # clean leading rubbish from number  | 
| 
251
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
252
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
         my $epoch = substr($buf, 0x24, 4);  | 
| 
253
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
41
 | 
         $record{epoch} =  | 
| 
254
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x1000000 * ord(substr($epoch, 0, 1)) +  | 
| 
255
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x10000   * ord(substr($epoch, 1, 1)) +  | 
| 
256
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x100     * ord(substr($epoch, 2, 1)) +  | 
| 
257
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                              ord(substr($epoch, 3, 1)) -  | 
| 
258
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  2082844800;  | 
| 
259
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
44
 | 
         my $dt = DateTime->from_epoch(  | 
| 
260
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             epoch => $record{epoch},  | 
| 
261
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             time_zone => $timezone  | 
| 
262
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         );  | 
| 
263
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2947
 | 
         $record{date} = sprintf('%04d-%02d-%02d', $dt->year(), $dt->month(), $dt->day());  | 
| 
264
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
99
 | 
         $record{time} = sprintf('%02d:%02d', $dt->hour(), $dt->minute());  | 
| 
265
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
266
 | 
7
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
80
 | 
         if($num eq '') {  | 
| 
267
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             delete @record{qw(epoch date time)};  | 
| 
268
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $type = 'unknown';  | 
| 
269
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
270
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif($type == 0x0000 && substr($buf, 0x0040, 1) ne "\00") {  | 
| 
271
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
         $dir = 'outbound';  | 
| 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
273
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # message first, preceded by 0x2002 and 16 bit length  | 
| 
274
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
130
 | 
         ($msg = $buf) =~ s/^.*\040\02..//s;  | 
| 
275
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
54
 | 
         $msg =~ s/\00.*//s;  | 
| 
276
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
277
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # then some cruft, ASCIIZ number and name  | 
| 
278
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # find number by finding *last* sequence of 6 or more digits, then  | 
| 
279
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # going back 1 to find a + if it's there  | 
| 
280
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
490
 | 
         ($num, $name) = split(/\00/, ($buf =~ /(\+?\d{6,}\00[^\00]+\00)/g)[-1]);  | 
| 
281
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
282
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
58
 | 
         my $epoch = substr($buf, index($buf, "\x80\00") + 2, 4);  | 
| 
283
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
61
 | 
         $record{epoch} =  | 
| 
284
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x1000000 * ord(substr($epoch, 0, 1)) +  | 
| 
285
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x10000   * ord(substr($epoch, 1, 1)) +  | 
| 
286
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x100     * ord(substr($epoch, 2, 1)) +  | 
| 
287
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                              ord(substr($epoch, 3, 1)) -  | 
| 
288
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  2082844800;  | 
| 
289
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
70
 | 
         my $dt = DateTime->from_epoch(  | 
| 
290
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             epoch => $record{epoch},  | 
| 
291
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             time_zone => $timezone  | 
| 
292
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         );  | 
| 
293
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5831
 | 
         $record{date} = sprintf('%04d-%02d-%02d', $dt->year(), $dt->month(), $dt->day());  | 
| 
294
 | 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
180
 | 
         $record{time} = sprintf('%02d:%02d', $dt->hour(), $dt->minute());  | 
| 
295
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
296
 | 
14
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
151
 | 
         if($num eq '') {  | 
| 
297
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             delete @record{qw(epoch date time)};  | 
| 
298
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             $type = 'unknown';  | 
| 
299
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
300
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif($type == 0x0000) {  | 
| 
301
 | 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
503
 | 
         $dir = 'outbound';  | 
| 
302
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
303
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # number field at 0x4C, possibly including some leading crap  | 
| 
304
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # then an ASCIIZ number  | 
| 
305
 | 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2853
 | 
         ($num  = substr($buf, 0x4C)) =~ s/(^\00*[^\00]+)\00.*/$1/s;  | 
| 
306
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
307
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # immediately followed by ASCIIZ name, with some trailing 0s  | 
| 
308
 | 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1581
 | 
         ($name = substr($buf, length($num) + 0x4C + 1)) =~ s/\00.*//s;  | 
| 
309
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
310
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         # ASCIIZ message, prefixed by 0x20 0x02 16-bit length word  | 
| 
311
 | 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
827
 | 
         $msg = substr($buf, length($num) + 0x4C + 1 + length($name) + 1);  | 
| 
312
 | 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3851
 | 
         $msg =~ s/^.*\x20\x02..|\00.*$//g;  | 
| 
313
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           | 
| 
314
 | 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
688
 | 
         $num =~ s/^[^0-9+]+//; # clean leading rubbish from number  | 
| 
315
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
316
 | 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
535
 | 
         my $epoch = substr($buf, 0x24, 4);  | 
| 
317
 | 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1310
 | 
         $record{epoch} =  | 
| 
318
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x1000000 * ord(substr($epoch, 0, 1)) +  | 
| 
319
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x10000   * ord(substr($epoch, 1, 1)) +  | 
| 
320
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  0x100     * ord(substr($epoch, 2, 1)) +  | 
| 
321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                              ord(substr($epoch, 3, 1)) -  | 
| 
322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                  2082844800;  | 
| 
323
 | 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1280
 | 
         my $dt = DateTime->from_epoch(  | 
| 
324
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             epoch => $record{epoch},  | 
| 
325
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             time_zone => $timezone  | 
| 
326
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         );  | 
| 
327
 | 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
237187
 | 
         $record{date} = sprintf('%04d-%02d-%02d', $dt->year(), $dt->month(), $dt->day());  | 
| 
328
 | 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4430
 | 
         $record{time} = sprintf('%02d:%02d', $dt->hour(), $dt->minute());  | 
| 
329
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
330
 | 
343
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
3582
 | 
         if($num eq '') {  | 
| 
331
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
32
 | 
             delete @record{qw(epoch date time)};  | 
| 
332
 | 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
30
 | 
             $type = 'unknown';  | 
| 
333
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
334
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
335
 | 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
100
 | 
         $type = 'unknown';  | 
| 
336
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
337
 | 
4193
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
23971
 | 
     $record{debug} = "\n".Data::Hexdumper::hexdump(suppress_warnings => 1, data => $buf) if($debug);  | 
| 
338
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1793793
 | 
     $record{device}    = 'Treo 680';  | 
| 
339
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5101
 | 
     $record{direction} = $dir;  # inbound or outbound  | 
| 
340
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5839
 | 
     $record{phone}     = $record{number} = $num;  | 
| 
341
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6793
 | 
     $record{timestamp} = $record{epoch};  | 
| 
342
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5199
 | 
     $record{name}      = $name;  | 
| 
343
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4457
 | 
     $record{text}      = $msg;  | 
| 
344
 | 
4193
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
12150
 | 
     $record{type}      = $type eq 'unknown' ? $type : sprintf('0x%04X', $type);  | 
| 
345
 | 
4193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8800
 | 
     return \%record;  | 
| 
346
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
347
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
348
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 BUGS, LIMITATIONS and FEEDBACK  | 
| 
349
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
350
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 The database structure is undocumented.  Consequently it has had to be  | 
| 
351
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 reverse-engineered.  There appear to be several message formats in  | 
| 
352
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 the database.  Some have a superficial resemblance to those used by  | 
| 
353
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 the 650 (and which is partially documented by Palm) but there is no  | 
| 
354
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 publicly available documentation that I could find for the others -  | 
| 
355
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 if you know where I can get docs, please let me know!  | 
| 
356
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
357
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 I can only reverse-engineer record formats that appear on my phone, so  | 
| 
358
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 there may be some missing.  In addition, I may decode some formats  | 
| 
359
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 incorrectly because they're not quite what I thought they were.  If  | 
| 
360
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 this affects you, please please please send me the offending data.  | 
| 
361
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
362
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 There is currently no support for creating a new database, or for  | 
| 
363
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 editing the contents of an existing database.  If you need that  | 
| 
364
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 functionality, please submit a patch with tests.  I will *not* write  | 
| 
365
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 this myself unless I need it.  Behaviour if you try to create or  | 
| 
366
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 edit a database is currently undefined, but editing a database will  | 
| 
367
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 almost certainly break it.  | 
| 
368
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
369
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 If you find any bugs please report them either using  | 
| 
370
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 L or by email.  Ideally, I would like to receive  | 
| 
371
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sample data and a test file, which fails with the latest version of  | 
| 
372
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 the module but will pass when I fix the bug.  | 
| 
373
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
374
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Sample data can be either in the form of a complete database, or a  | 
| 
375
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 dump of just a single record structure, which *must* include the  | 
| 
376
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 raw binary data -  | 
| 
377
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 use the 'incl_raw' option when you load the module, and save the  | 
| 
378
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 data structure to a file using Data::Dumper.  | 
| 
379
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Feel free to obscure  | 
| 
380
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 real names, phone numbers, and messages in the data, but you  | 
| 
381
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 should ensure that phone numbers are correctly formed, and that  | 
| 
382
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 you don't change the length of any parts of the message.  Also,  | 
| 
383
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 please don't change any non-human-readable parts of the record.  | 
| 
384
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
385
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 SEE ALSO  | 
| 
386
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
387
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 L, which handles SMS messages databases on some other models  | 
| 
388
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 of Treo.  | 
| 
389
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
390
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 L  | 
| 
391
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 L  | 
| 
393
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
394
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 THANKS TO  | 
| 
395
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
396
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Michal Seliga, for sample MMS data  | 
| 
397
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
398
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 AUTHOR, COPYRIGHT and LICENCE  | 
| 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
400
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 Copyright 2008 David Cantrell Edavid@cantrell.org.ukE  | 
| 
401
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
402
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This software is free-as-in-speech software, and may be used,  | 
| 
403
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 distributed, and modified under the terms of either the GNU  | 
| 
404
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 General Public Licence version 2 or the Artistic Licence. It's  | 
| 
405
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 up to you which one you use. The full text of the licences can  | 
| 
406
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 be found in the files GPL2.txt and ARTISTIC.txt, respectively.  | 
| 
407
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
408
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =head1 CONSPIRACY  | 
| 
409
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
410
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 This module is also free-as-in-mason software.  | 
| 
411
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
412
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 =cut  | 
| 
413
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
414
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  |