3
/** We rely on the Data_palm:: abstract class. */
4
require_once dirname(__FILE__) . '/palm.php';
7
* Class to allow data exchange between the Horde applications and
8
* palm pdb datebok file.
10
* $Horde: framework/Data/Data/pdb.php,v 1.7 2004/02/15 03:50:06 chuck Exp $
14
* @author Mathieu Clabaut <mathieu.clabaut@free.fr>
17
class Data_pdb extends Data_palm {
19
var $_extension = 'pdb';
22
function importFile($filename)
24
$this->_pdb = &new PalmDatebook();
25
$fp = fopen($filename, 'r');
27
$this->_pdb->ReadFile($fp);
29
return $this->import();
37
$nbrec = $this->_pdb->GetRecordCount();
40
foreach ($this->_pdb->GetRecordIDs() as $id) {
42
$record = $this->_pdb->GetRecordRaw($id);
43
$row['title'] = $record['Description'];
44
$row['category'] = 'Palm';
45
if (!empty($record['Note'])) {
46
$row['description'] = $record['Note'];
48
$row['start_date'] = $record['Date'];
49
$row['end_date'] = $row['start_date'];
50
if (!empty($record['StartTime'])) {
51
$row['start_time'] = $record['StartTime'] . ":00";
52
if (!empty($record['EndTime'])) {
53
$row['end_time'] = $record['EndTime'] . ":00" ;
56
$row['start_time'] = "00:00:00";
57
$row['end_time'] = "00:00:00";
58
$date = explode('-', $row['start_date']);
59
$date[2] = 1 + $date[2];
60
$row['end_date'] = implode('-', $date);
62
if (!empty($record['Alarm']) &&
63
preg_match('/(\d+)([dmh])/', $record['Alarm'], $matches)) {
64
switch ($matches[2]) {
66
$row['alarm'] = $matches[1];
70
$row['alarm'] = $matches[1]*60;
74
$row['alarm'] = $matches[1]*60*24;
79
if (!empty($record['Repeat'])) {
80
switch ($record['Repeat']['Type']) {
81
case PDB_DATEBOOK_REPEAT_NONE:
82
$row['recur_type'] = KRONOLITH_RECUR_NONE;
85
case PDB_DATEBOOK_REPEAT_DAILY:
86
$row['recur_type'] = KRONOLITH_RECUR_DAILY;
87
if (!empty($record['Repeat']['Date'])) {
88
$row['recur_end_date'] = $record['Repeat']['Date'];
90
$row['recur_interval'] = $record['Repeat']['Frequency'];
93
case PDB_DATEBOOK_REPEAT_WEEKLY:
94
$row['recur_type'] = KRONOLITH_RECUR_WEEKLY;
95
if (!empty($record['Repeat']['Date'])) {
96
$row['recur_end_date'] = $record['Repeat']['Date'];
98
$row['recur_interval'] = $record['Repeat']['Frequency'];
99
if (!empty($record['Repeat']['Days'])) {
101
for ($c = 0; $c < strlen($record['Repeat']['Days']); $c++) {
102
$days = $days | (int)pow(2, (int)$record['Repeat']['Days']{$c});
104
$row['recur_data'] = $days;
108
case PDB_DATEBOOK_REPEAT_MONTH_BY_DAY:
109
// TODO : to be completed with
110
// $record['Repeat']['WeekNum'] and
111
// $record['Repeat']['DayNum'].
112
$row['recur_type'] = KRONOLITH_RECUR_DAY_OF_MONTH;
113
if (!empty($record['Repeat']['Date'])) {
114
$row['recur_end_date'] = $record['Repeat']['Date'];
116
$row['recur_interval'] = $record['Repeat']['Frequency'];
119
case PDB_DATEBOOK_REPEAT_MONTH_BY_DATE:
120
// TODO, verify the compliance with lib/Kronolith.php
121
$row['recur_type'] = KRONOLITH_RECUR_WEEK_OF_MONTH;
122
if (!empty($record['Repeat']['Date'])) {
123
$row['recur_end_date'] = $record['Repeat']['Date'];
125
$row['recur_interval'] = $record['Repeat']['Frequency'];
128
case PDB_DATEBOOK_REPEAT_YEARLY:
129
$row['recur_type'] = KRONOLITH_RECUR_YEARLY;
130
if (!empty($record['Repeat']['Date'])) {
131
$row['recur_end_date'] = $record['Repeat']['Date'];
133
$row['recur_interval'] = $record['Repeat']['Frequency'];
146
* Class extender for PalmOS Datebook files
148
* Copyright (C) 2001 - PHP-PDB development team
149
* Licensed under the GNU LGPL
150
* See the doc/LEGAL file for more information
151
* See http://php-pdb.sourceforge.net/ for more information about the library
155
define('PDB_DATEBOOK_REPEAT_NONE', 0);
156
define('PDB_DATEBOOK_REPEAT_DAILY', 1);
157
define('PDB_DATEBOOK_REPEAT_WEEKLY', 2);
158
define('PDB_DATEBOOK_REPEAT_MONTH_BY_DAY', 3);
159
define('PDB_DATEBOOK_REPEAT_MONTH_BY_DATE', 4);
160
define('PDB_DATEBOOK_REPEAT_YEARLY', 5);
164
define('PDB_DATEBOOK_FLAG_DESCRIPTION', 1024); // Record has description
165
// (mandatory, as far as I know)
166
define('PDB_DATEBOOK_FLAG_EXCEPTIONS', 2048); // Are there any exceptions?
167
define('PDB_DATEBOOK_FLAG_NOTE', 4096); // Is there an associated note?
168
define('PDB_DATEBOOK_FLAG_REPEAT', 8192); // Does the event repeat?
169
define('PDB_DATEBOOK_FLAG_ALARM', 16384); // Is there an alarm set?
170
define('PDB_DATEBOOK_FLAG_WHEN', 32768); // Was the 'when' updated?
171
// (Internal use only?)
174
/* The data for SetRecordRaw and from GetRecordRaw should be/return a
175
* special array, detailed below. Optional values can be set to '' or not
176
* defined. If they are anything else (including zero), they are considered
177
* to be 'set'. Optional values are marked with a ^.
179
* Key Example Description
180
* ------------------------------------------
181
* StartTime 2:00 Starting time of event, 24 hour format
182
* EndTime 13:00 Ending time of event, 24 hour format
183
* Date 2001-01-23 Year-Month-Day of event
184
* Description Title This is the title of the event
185
* Alarm 5d ^ Number of units (m=min, h=hours, d=days)
186
* Repeat ??? ^ Repeating event data (array)
187
* Note NoteNote ^ A note for the event
188
* Exceptions ??? ^ Exceptions to the event
189
* WhenChanged ??? ^ True if "when info" for event has changed
190
* Flags 3 ^ User flags (highest bit allowed is 512)
192
* EndTime must happen at or after StartTime. The time the event occurs
193
* may not pass midnight (0:00). If the event doesn't have a time, use ''
194
* or do not define StartTime and EndTime.
198
* No repeat (or leave the array undefined):
199
* $repeat['Type'] = PDB_DATEBOOK_REPEAT_NONE;
201
* Daily repeating events:
202
* $repeat['Type'] = PDB_DATEBOOK_REPEAT_DAILY;
203
* $repeat['Frequency'] = FREQ; // Explained below
204
* $repeat['End'] = END_DATE; // Explained below
206
* Weekly repeating events:
207
* $repeat['Type'] = PDB_DATEBOOK_REPEAT_WEEKLY;
208
* $repeat['Frequency'] = FREQ; // Explained below
209
* $repeat['Days'] = DAYS; // Explained below
210
* $repeat['End'] = END_DATE; // Explained below
211
* $repeat['StartOfWeek'] = SOW; // Explained below
213
* "Monthly by day" repeating events:
214
* $repeat['Type'] = PDB_DATEBOOK_REPEAT_MONTH_BY_DAY;
215
* $repeat['WeekNum'] = WEEKNUM; // Explained below
216
* $repeat['DayNum'] = DAYNUM; // Explained below
217
* $repeat['Frequency'] = FREQ; // Explained below
218
* $repeat['End'] = END_DATE; // Explained below
220
* "Monthly by date" repeating events:
221
* $repeat['Type'] = PDB_DATEBOOK_REPEAT_MONTH_BY_DATE;
222
* $repeat['Frequency'] = FREQ; // Explained below
223
* $repeat['End'] = END_DATE; // Explained below
225
* Yearly repeating events:
226
* $repeat['Type'] = PDB_DATEBOOK_REPEAT_YEARLY;
227
* $repeat['Frequency'] = FREQ; // Explained below
228
* $repeat['End'] = END_DATE; // Explained below
230
* There is also two mysterious 'unknown' fields for the repeat event that
231
* will be populated if the database is loaded from a file. They will
232
* otherwise default to 0. They are 'unknown1' and 'unknown2'.
234
* FREQ = Frequency of the event. If it is a daily event and you want it
235
* to happen every other day, set Frequency to 2. This will default
236
* to 1 if not specified.
237
* END_DATE = The last day, month, and year on which the event occurs.
238
* Format is YYYY-MM-DD. If not specified, no end date will
240
* DAYS = What days during the week the event occurs. This is a string of
241
* numbers from 0 - 6. I'm not sure if 0 = Sunday or if 0 =
242
* start of week from the preferences.
243
* SOW = As quoted from P5-Palm: "I'm not sure what this is, but the
244
* Datebook app appears to perform some hairy calculations
246
* WEEKNUM = The number of the week on which the event occurs. 5 is the
247
* last week of the month.
248
* DAYNUM = The day of the week on which the event occurs. Again, I don't
249
* know if 0 = Sunday or if 0 = start of week from the prefs.
251
* Exceptions are specified in an array consisting of dates the event occured
252
* and did not happen or should not be shown. Dates are in the format
255
* @package Horde_Data
257
class PalmDatebook extends PalmDB {
261
// Constructor -- initialize the default values
262
function PalmDatebook () {
263
PalmDB::PalmDB('DATA', 'date', 'DatebookDB');
268
// Returns an array with default data for a new record.
269
// This doesn't actually add the record.
270
function NewRecord() {
271
// Default event is untimed
272
// Event's date is today
273
$Event['Date'] = date("Y-m-d");
275
// Set an alarm 10 min before the event
276
$Event['Alarm'] = '10m';
282
// Converts a date string ( YYYY-MM-DD )( "2001-10-31" )
283
// into bitwise ( YYYY YYYM MMMD DDDD )
284
// Should only be used when saving
285
function DateToInt16($date) {
286
$YMD = explode('-', $date);
287
return ($YMD[0] - 1904) * 512 + $YMD[1] * 32 + $YMD[2];
291
// Converts a bitwise date ( YYYY YYYM MMMD DDDD )
292
// Into the human readable date string ( YYYY-MM-DD )( "2001-10-31" )
293
// Should only be used when loading
294
function Int16ToDate($number) {
295
$year = $number / 512;
296
settype($year, "integer");
298
$number = $number % 512;
299
$month = $number / 32;
300
settype($month, "integer");
302
return $year . '-' . $month . '-' . $day;
306
// Prepares the record flags for the specified record;
307
// Should only be used when saving
308
function GetRecordFlags(& $data) {
309
if (! isset($data['Flags']))
311
$Flags = $data['Flags'] % 1024;
312
if (isset($data['Description']) && $data['Description'] != '')
313
$Flags += PDB_DATEBOOK_FLAG_DESCRIPTION;
314
if (isset($data['Exceptions']) && is_array($data['Exceptions']) &&
315
count($data['Exceptions']) > 0)
316
$Flags += PDB_DATEBOOK_FLAG_EXCEPTIONS;
317
if (isset($data['Note']) && $data['Note'] != '')
318
$Flags += PDB_DATEBOOK_FLAG_NOTE;
319
if (isset($data['Repeat']) && is_array($data['Repeat']) &&
320
count($data['Repeat']) > 0)
321
$Flags += PDB_DATEBOOK_FLAG_REPEAT;
322
if (isset($data['Alarm']) && $data['Alarm'] != '' &&
323
preg_match('/^([0-9]+)([mMhHdD])$/', $data['Alarm'], $AlarmMatch))
324
$Flags += PDB_DATEBOOK_FLAG_ALARM;
325
if (isset($data['WhenChanged']) && $data['WhenChanged'] != '' &&
326
$data['WhenChanged'])
327
$Flags += PDB_DATEBOOK_FLAG_WHEN;
329
$data['Flags'] = $Flags;
333
// Overrides the GetRecordSize method.
334
// Probably should only be used when saving
335
function GetRecordSize($num = false) {
337
$num = $this->CurrentRecord;
339
if (! isset($this->Records[$num]) || ! is_array($this->Records[$num]))
340
return PalmDB::GetRecordSize($num);
342
$data = $this->Records[$num];
344
// Start Time and End Time (4)
345
// The day of the event (2)
348
$this->GetRecordFlags($data);
350
if ($data['Flags'] & PDB_DATEBOOK_FLAG_ALARM)
353
if ($data['Flags'] & PDB_DATEBOOK_FLAG_REPEAT)
356
if ($data['Flags'] & PDB_DATEBOOK_FLAG_EXCEPTIONS)
357
$Bytes += 2 + count($data['Exceptions']) * 2;
359
if ($data['Flags'] & PDB_DATEBOOK_FLAG_DESCRIPTION)
360
$Bytes += strlen($data['Description']) + 1;
362
if ($data['Flags'] & PDB_DATEBOOK_FLAG_NOTE)
363
$Bytes += strlen($data['Note']) + 1;
369
// Overrides the GetRecord method. We store data in associative arrays.
370
// Just convert the data into the proper format and then return the
372
function GetRecord($num = false) {
374
$num = $this->CurrentRecord;
376
if (! isset($this->Records[$num]) || ! is_array($this->Records[$num]))
377
return PalmDB::GetRecord($num);
379
$data = $this->Records[$num];
383
// Start Time and End Time
385
// 0xFFFFFFFF if the event has no time
386
if (! isset($data['StartTime']) || ! isset($data['EndTime']) ||
387
strpos($data['StartTime'], ':') === false ||
388
strpos($data['EndTime'], ':') === false) {
389
$RecordString .= $this->Int16(65535);
390
$RecordString .= $this->Int16(65535);
392
list($StartH, $StartM) = explode(':', $data['StartTime']);
393
list($EndH, $EndM) = explode(':', $data['EndTime']);
394
if ($StartH < 0 || $StartH > 23 || $StartM < 0 || $StartM > 59 ||
395
$EndH < 0 || $EndH > 23 || $EndM < 0 || $EndM > 59) {
396
$RecordString .= $this->Int16(65535);
397
$RecordString .= $this->Int16(65535);
399
if ($EndH < $StartH || ($EndH == $StartH && $EndM < $StartM)) {
406
$RecordString .= $this->Int8($StartH);
407
$RecordString .= $this->Int8($StartM);
408
$RecordString .= $this->Int8($EndH);
409
$RecordString .= $this->Int8($EndM);
413
// The day of the event
414
// For repeating events, this is the first day the event occurs
415
$RecordString .= $this->Int16($this->DateToInt16($data['Date']));
418
$this->GetRecordFlags($data);
419
$Flags = $data['Flags'];
420
$RecordString .= $this->Int16($Flags);
422
if ($Flags & PDB_DATEBOOK_FLAG_ALARM &&
423
preg_match('/^([0-9]+)([mMhHdD])$/', $data['Alarm'], $AlarmMatch)) {
424
$RecordString .= $this->Int8($AlarmMatch[1]);
425
$AlarmMatch[2] = strtolower($AlarmMatch[2]);
426
if ($AlarmMatch[2] == 'm')
427
$RecordString .= $this->Int8(0);
428
elseif ($AlarmMatch[2] == 'h')
429
$RecordString .= $this->Int8(1);
431
$RecordString .= $this->Int8(2);
434
if ($Flags & PDB_DATEBOOK_FLAG_REPEAT) {
435
$d = $data['Repeat'];
437
$RecordString .= $this->Int8($d['Type']);
439
if (! isset($d['unknown1']))
441
$RecordString .= $this->Int8($d['unknown1']);
443
if (isset($d['End']))
444
$RecordString .= $this->Int16($this->DateToInt16($d['End']));
446
$RecordString .= $this->Int16(65535);
448
if (! isset($d['Frequency']))
450
$RecordString .= $this->Int8($d['Frequency']);
452
if ($d['Type'] == PDB_DATEBOOK_REPEAT_WEEKLY) {
455
$QuickLookup = array(1, 2, 4, 8, 16, 32, 64);
457
while ($i < strlen($days)) {
459
settype($num, 'integer');
460
if (isset($QuickLookup[$num]))
461
$flags = $flags | $QuickLookup[$num];
464
$RecordString .= $this->Int8($flags);
465
if (isset($d['StartOfWeek']) && $d['StartOfWeek'] != '')
466
$RecordString .= $this->Int8($d['StartOfWeek']);
468
$RecordString .= $this->Int8(0);
469
} elseif ($d['Type'] == PDB_DATEBOOK_REPEAT_MONTH_BY_DAY) {
470
if ($d['WeekNum'] > 5)
472
$RecordString .= $this->Int8($d['WeekNum'] * 7 + $d['DayNum']);
473
$RecordString .= $this->Int8(0);
475
$RecordString .= $this->Int16(0);
477
if (! isset($d['unknown2']))
479
$RecordString .= $this->Int8($d['unknown2']);
482
if ($Flags & PDB_DATEBOOK_FLAG_EXCEPTIONS) {
483
$d = $data['Exceptions'];
484
$RecordString .= $this->Int16(count($d));
485
foreach ($d as $exception) {
486
$RecordString .= $this->Int16($this->DateToInt16($exception));
490
if ($Flags & PDB_DATEBOOK_FLAG_DESCRIPTION) {
491
$RecordString .= $this->String($data['Description']);
492
$RecordString .= $this->Int8(0);
495
if ($Flags & PDB_DATEBOOK_FLAG_NOTE) {
496
$RecordString .= $this->String($data['Note']);
497
$RecordString .= $this->Int8(0);
500
return $RecordString;
504
// Returns the size of the AppInfo block. It is the size of the
505
// category list plus four bytes.
506
function GetAppInfoSize() {
507
return PDB_CATEGORY_SIZE + 4;
511
// Returns the AppInfo block. It is composed of the category list (which
512
// doesn't seem to be used and is just filled with NULL bytes) and four
513
// bytes that specify the first day of the week. Not sure what that
514
// value is supposed to be, so I just use zero.
515
function GetAppInfo() {
516
// Category list (Nulls)
517
$this->AppInfo = $this->PadString('', PDB_CATEGORY_SIZE);
519
// Unknown thing (first_day_in_week)
520
// 00 00 FD 00 == where FD is the first day in week.
521
// I'm using 0 as the default value since I don't know what it should be
522
$this->AppInfo .= $this->Int16(0);
523
$this->AppInfo .= $this->Int8($this->FirstDay);
524
$this->AppInfo .= $this->Int8(0);
526
return $this->AppInfo;
530
// Parses $fileData for the information we need when loading a datebook
532
function LoadAppInfo($fileData) {
533
$fileData = substr($fileData, PDB_CATEGORY_SIZE + 2);
534
if (strlen($fileData < 1))
536
$this->FirstDay = $this->LoadInt8($fileData);
540
// Converts the datebook record data loaded from a file into the internal
541
// storage method that is used for the rest of the class and for ease of
543
// Return false to signal no error
544
function LoadRecord($fileData, $RecordInfo) {
545
$this->RecordAttrs[$RecordInfo['UID']] = $RecordInfo['Attrs'];
547
$NewRec = $this->NewRecord();
548
$StartH = $this->LoadInt8(substr($fileData, 0, 1));
549
$StartM = $this->LoadInt8(substr($fileData, 1, 1));
550
$EndH = $this->LoadInt8(substr($fileData, 2, 1));
551
$EndM = $this->LoadInt8(substr($fileData, 3, 1));
552
if ($StartH != 255 && $StartM != 255) {
553
$NewRec['StartTime'] = $StartH . ':';
555
$NewRec['StartTime'] .= '0';
556
$NewRec['StartTime'] .= $StartM;
558
if ($EndH != 255 && $EndM != 255) {
559
$NewRec['EndTime'] = $EndH . ':';
561
$NewRec['EndTime'] .= '0';
562
$NewRec['EndTime'] .= $EndM;
564
$NewRec['Date'] = $this->LoadInt16(substr($fileData, 4, 2));
565
$NewRec['Date'] = $this->Int16ToDate($NewRec['Date']);
566
$Flags = $this->LoadInt16(substr($fileData, 6, 2));
567
$NewRec['Flags'] = $Flags;
568
$fileData = substr($fileData, 8);
570
if ($Flags & PDB_DATEBOOK_FLAG_WHEN)
571
$NewRec['WhenChanged'] = true;
573
if ($Flags & PDB_DATEBOOK_FLAG_ALARM) {
574
$amount = $this->LoadInt8(substr($fileData, 0, 1));
575
$unit = $this->LoadInt8(substr($fileData, 1, 1));
582
$NewRec['Alarm'] = $amount . $unit;
583
$fileData = substr($fileData, 2);
585
unset($NewRec['Alarm']);
587
if ($Flags & PDB_DATEBOOK_FLAG_REPEAT) {
589
$Repeat['Type'] = $this->LoadInt8(substr($fileData, 0, 1));
590
$Repeat['unknown1'] = $this->LoadInt8(substr($fileData, 1, 1));
591
$End = $this->LoadInt16(substr($fileData, 2, 2));
592
$Repeat['Frequency'] = $this->LoadInt8(substr($fileData, 4, 1));
593
$RepeatOn = $this->LoadInt8(substr($fileData, 5, 1));
594
$RepeatSoW = $this->LoadInt8(substr($fileData, 6, 1));
595
$Repeat['unknown2'] = $this->LoadInt8(substr($fileData, 7, 1));
596
$fileData = substr($fileData, 8);
598
if ($End != 65535 && $End <= 0)
599
$Repeat['End'] = $this->Int16ToDate($End);
601
if ($Repeat['Type'] == PDB_DATEBOOK_REPEAT_WEEKLY) {
617
$Repeat['Days'] = $days;
618
$Repeat['StartOfWeek'] = $RepeatSoW;
619
} elseif ($Repeat['Type'] == PDB_DATEBOOK_REPEAT_MONTH_BY_DAY) {
620
$Repeat['DayNum'] = $RepeatOn % 7;
622
settype($RepeatOn, 'integer');
623
$Repeat['WeekNum'] = $RepeatOn;
626
$NewRec['Repeat'] = $Repeat;
629
if ($Flags & PDB_DATEBOOK_FLAG_EXCEPTIONS) {
630
$Exceptions = array();
631
$number = $this->LoadInt16(substr($fileData, 0, 2));
632
$fileData = substr($fileData, 2);
634
$Exc = $this->LoadInt16(substr($fileData, 0, 2));
635
$Exceptions[] = $this->Int16ToDate($Exc);
636
$fileData = substr($fileData, 2);
638
$NewRec['Exceptions'] = $Exceptions;
641
if ($Flags & PDB_DATEBOOK_FLAG_DESCRIPTION) {
643
$NewRec['Description'] = '';
644
while ($i < strlen($fileData) && $fileData[$i] != "\0") {
645
$NewRec['Description'] .= $fileData[$i];
648
$fileData = substr($fileData, $i + 1);
651
if ($Flags & PDB_DATEBOOK_FLAG_NOTE) {
653
$NewRec['Note'] = '';
654
while ($i < strlen($fileData) && $fileData[$i] != "\0") {
655
$NewRec['Note'] .= $fileData[$i];
658
$fileData = substr($fileData, 0, $i + 1);
661
$this->Records[$RecordInfo['UID']] = $NewRec;