1
// *************************************************************************
4
// * File: gsm_phonebook.cc
6
// * Purpose: Phonebook management functions
8
// * Author: Peter Hofmann (software@pxh.de)
10
// * Created: 6.5.1999
11
// *************************************************************************
14
#include <gsm_config.h>
16
#include <gsmlib/gsm_nls.h>
17
#include <gsmlib/gsm_sysdep.h>
18
#include <gsmlib/gsm_phonebook.h>
19
#include <gsmlib/gsm_parser.h>
20
#include <gsmlib/gsm_me_ta.h>
27
using namespace gsmlib;
29
// PhonebookEntry members
31
PhonebookEntry::PhonebookEntry(const PhonebookEntryBase &e)
32
throw(GsmException) : _cached(true), _myPhonebook(NULL)
34
set(e.telephone(), e.text(), e.index(), e.useIndex());
37
void PhonebookEntry::set(string telephone, string text, int index,
41
checkTextAndTelephone(text, telephone);
43
if (_myPhonebook != NULL)
45
if (text.length() > _myPhonebook->getMaxTextLen())
47
stringPrintf(_("length of text '%s' exceeds maximum text "
48
"length (%d characters) of phonebook '%s'"),
49
text.c_str(), _myPhonebook->getMaxTextLen(),
50
_myPhonebook->name().c_str()),
53
if (telephone.length() > _myPhonebook->getMaxTelephoneLen())
55
stringPrintf(_("length of telephone number '%s' "
56
"exceeds maximum telephone number "
57
"length (%d characters) of phonebook '%s'"),
58
telephone.c_str(), _myPhonebook->getMaxTelephoneLen(),
59
_myPhonebook->name().c_str()),
62
_myPhonebook->writeEntry(_index, telephone, text);
69
_telephone = telephone;
74
string PhonebookEntry::text() const throw(GsmException)
78
assert(_myPhonebook != NULL);
79
// these operations are at least "logically const"
80
PhonebookEntry *thisEntry = const_cast<PhonebookEntry*>(this);
81
_myPhonebook->readEntry(_index, thisEntry->_telephone, thisEntry->_text);
82
thisEntry->_cached = true;
87
string PhonebookEntry::telephone() const throw(GsmException)
91
assert(_myPhonebook != NULL);
92
// these operations are at least "logically const"
93
PhonebookEntry *thisEntry = const_cast<PhonebookEntry*>(this);
94
_myPhonebook->readEntry(_index, thisEntry->_telephone, thisEntry->_text);
95
thisEntry->_cached = true;
100
bool PhonebookEntry::cached() const
102
if (_myPhonebook == NULL)
105
return _cached && _myPhonebook->_useCache;
108
PhonebookEntry::PhonebookEntry(const PhonebookEntry &e) throw(GsmException)
110
set(e._telephone, e._text, e._index, e._useIndex);
113
PhonebookEntry &PhonebookEntry::operator=(const PhonebookEntry &e)
116
set(e._telephone, e._text, e._index, e._useIndex);
122
int Phonebook::parsePhonebookEntry(string response,
123
string &telephone, string &text)
125
// this is a workaround for a bug that occurs with my ME/TA combination
126
// some texts are truncated and don't have a trailing "
127
if (response.length() > 0 && response[response.length() - 1] != '"')
131
int index = p.parseInt();
134
// handle case of empty entry
135
if (p.getEol().substr(0, 5) == "EMPTY")
142
telephone = p.parseString();
144
unsigned int numberFormat = p.parseInt();
145
if (numberFormat != UnknownNumberFormat &&
146
numberFormat != InternationalNumberFormat)
147
cerr << "*** GSMLIB WARNING: Unexpected number format when reading from "
148
<< "phonebook: " << numberFormat << " ***" << endl;
150
text = p.parseString(false, true);
151
if (lowercase(_myMeTa.getCurrentCharSet()) == "gsm")
152
text = gsmToLatin1(text);
153
if (numberFormat == InternationalNumberFormat)
155
// skip leading "+" signs that may already exist
156
while (telephone.length() > 0 && telephone[0] == '+')
157
telephone = telephone.substr(1);
158
telephone = "+" + telephone;
164
void Phonebook::readEntry(int index, string &telephone, string &text)
168
_myMeTa.setPhonebook(_phonebookName);
171
string response = _at->chat("+CPBR=" + intToStr(index), "+CPBR:",
172
false, // dont't ignore errors
173
true); // but accept empty responses
174
// (the latter is necessary for some mobile phones that return nothing
175
// if the entry is empty)
177
if (response.length() == 0) // OK phone returned empty response
179
telephone = text = ""; // then the entry is empty as well
182
parsePhonebookEntry(response, telephone, text);
185
if (debugLevel() >= 1)
186
cerr << "*** Reading PB entry " << index << " number " << telephone
187
<< " text " << text << endl;
191
void Phonebook::findEntry(string text, int &index, string &telephone)
195
_myMeTa.setPhonebook(_phonebookName);
198
string response = _at->chat("+CPBF=\"" + text + "\"", "+CPBF:",
199
false, // dont't ignore errors
200
true); // but accept empty responses
201
// (the latter is necessary for some mobile phones that return nothing
202
// if the entry is empty)
204
if (response.length() == 0) // OK phone returned empty response
206
telephone = ""; // then the entry is empty as well
210
index=parsePhonebookEntry(response, telephone, text);
213
if (debugLevel() >= 1)
214
cerr << "*** Finding PB entry " << text << " number " << telephone
215
<< " index " << index << endl;
219
void Phonebook::writeEntry(int index, string telephone, string text)
223
if (debugLevel() >= 1)
224
cerr << "*** Writing PB entry #" << index << " number '" << telephone
225
<< "' text '" << text << "'" << endl;
228
_myMeTa.setPhonebook(_phonebookName);
232
if (telephone == "" && text == "")
235
os << "+CPBW=" << index;
244
if (telephone.find('+') == string::npos)
245
type = UnknownNumberFormat;
247
type = InternationalNumberFormat;
248
string gsmText = text;
249
if (lowercase(_myMeTa.getCurrentCharSet()) == "gsm")
250
gsmText = latin1ToGsm(gsmText);
252
os << "+CPBW=" << index << ",\"" << telephone << "\"," << type
258
// this cannot be added with ostrstream because the gsmText can
259
// contain a zero (GSM default alphabet for '@')
265
Phonebook::iterator Phonebook::insertFirstEmpty(string telephone, string text)
268
for (int i = 0; i < _maxSize; i++)
269
if (_phonebook[i].empty())
271
_phonebook[i].set(telephone, text);
275
throw GsmException(_("phonebook full"), OtherError);
278
Phonebook::iterator Phonebook::insert(const string telephone,
282
for (int i = 0; i < _maxSize; i++)
283
if (_phonebook[i].index() == index)
284
if (_phonebook[i].empty())
286
_phonebook[i].set(telephone, text);
291
throw GsmException(_("attempt to overwrite phonebook entry"),
296
Phonebook::Phonebook(string phonebookName, Ref<GsmAt> at, MeTa &myMeTa,
297
bool preload) throw(GsmException) :
298
_phonebookName(phonebookName), _at(at), _myMeTa(myMeTa), _useCache(true)
301
_myMeTa.setPhonebook(_phonebookName);
303
// query size and maximum capacity of phonebook
304
_size = -1; // -1 means not known yet
306
Parser q(_at->chat("+CPBS?", "+CPBS:"));
307
string dummy = q.parseString();
308
if (q.parseComma(true)) // this means that
309
{ // used and total result is supported by ME
310
_size = q.parseInt();
312
_maxSize = q.parseInt();
315
// get basic phonebook info from ME
316
Parser p(_at->chat("+CPBR=?", "+CPBR:"));
318
// get index of actually available entries in the phonebook
319
vector<bool> availablePositions = p.parseIntList();
321
_maxNumberLength = p.parseInt();
323
_maxTextLength = p.parseInt();
325
// find out capacity of phonebook in ME
326
// Note: The phonebook in the ME may be sparse, eg. the range of
327
// allowed index numbers may be something like (3-4, 20-100, 120).
328
// The standard allows this, even though it is unlikely to be
329
// implemented like that by anyone.
330
// In memory we store only phonebook entries that may actually be
331
// used, ie. the phonebook in memory is not sparse.
332
// Each entry has a member _index that corresponds to the index in the ME.
336
for (vector<bool>::iterator i = availablePositions.begin();
337
i != availablePositions.end(); ++i)
341
// for use with preload below
342
int *meToPhonebookIndexMap =
343
(int*)alloca(sizeof(int) * (availablePositions.size() + 1));
345
// initialize phone book entries
349
_phonebook = new PhonebookEntry[_maxSize];
350
int nextAvailableIndex = 0;
351
for (int i = 0; i < _maxSize; i++)
353
while (! availablePositions[nextAvailableIndex])
354
nextAvailableIndex++;
355
_phonebook[i]._index = nextAvailableIndex;
356
_phonebook[i]._cached = false;
357
_phonebook[i]._myPhonebook = this;
358
meToPhonebookIndexMap[nextAvailableIndex++] = i;
361
// find out first index number of phonebook
363
for (int i = 0; i < _maxSize; i++)
364
if (availablePositions[i])
371
// Note: this contains a workaround for the bug that
372
// some MEs can not return the entire phonebook with one AT command
373
// To detect this condition, _size must be known
374
// also, this code only handles non-sparse phonebooks
375
if (preload && _size != -1 &&
376
(int)availablePositions.size() == _maxSize + firstIndex)
379
int startIndex = firstIndex;
381
while (entriesRead < _size)
383
reportProgress(0, _maxSize); // chatv also calls reportProgress()
384
vector<string> responses =
385
_at->chatv("+CPBR=" + intToStr(startIndex) +
386
"," + intToStr(_maxSize + firstIndex - 1),
389
// this means that we have read nothing even though not all
390
// entries have been retrieved (entriesRead < _size)
391
// this could be due to a malfunction of the ME...
392
// anyway, missing entries can be read later by readEntry()
393
if (responses.size() == 0)
396
if (debugLevel() >= 1)
397
cerr << "*** error when preloading phonebook: "
398
"not all entries returned" << endl;
403
for (vector<string>::iterator i = responses.begin();
404
i != responses.end(); ++i)
406
string telephone, text;
407
int meIndex = parsePhonebookEntry(*i, telephone, text);
408
_phonebook[meToPhonebookIndexMap[meIndex]]._cached = true;
409
_phonebook[meToPhonebookIndexMap[meIndex]]._telephone = telephone;
410
_phonebook[meToPhonebookIndexMap[meIndex]]._text = text;
411
assert(_phonebook[meToPhonebookIndexMap[meIndex]]._index == meIndex);
414
startIndex = meIndex + 1;
416
if (debugLevel() >= 1)
417
cerr << "*** Preloading PB entry " << meIndex
418
<< " number " << telephone
419
<< " text " << text << endl;
426
Phonebook::iterator Phonebook::begin()
428
return &_phonebook[0];
431
Phonebook::const_iterator Phonebook::begin() const
433
return &_phonebook[0];
436
Phonebook::iterator Phonebook::end()
438
return &_phonebook[_maxSize];
441
Phonebook::const_iterator Phonebook::end() const
443
return &_phonebook[_maxSize];
446
Phonebook::reference Phonebook::operator[](int n)
448
return _phonebook[n];
451
Phonebook::const_reference Phonebook::operator[](int n) const
453
return _phonebook[n];
456
Phonebook::reference Phonebook::front()
458
return _phonebook[0];
461
Phonebook::const_reference Phonebook::front() const
463
return _phonebook[0];
466
Phonebook::reference Phonebook::back()
468
return _phonebook[_maxSize - 1];
471
Phonebook::const_reference Phonebook::back() const
473
return _phonebook[_maxSize - 1];
476
int Phonebook::size() const throw(GsmException)
483
for (int i = 0; i < _maxSize; i++)
484
if (! _phonebook[i].empty())
486
Phonebook *thisPhonebook = const_cast<Phonebook*>(this);
487
thisPhonebook->_size = result;
492
Phonebook::iterator Phonebook::insert(iterator position,
493
const PhonebookEntry& x)
496
if (x.useIndex() && x.index() != -1)
497
return insert(x.telephone(), x.text(), x.index());
499
return insertFirstEmpty(x.telephone(), x.text());
502
void Phonebook::insert (iterator pos, int n, const PhonebookEntry& x)
505
for (int i = 0; i < n; i++)
506
if (x.useIndex() && x.index() != -1)
507
insert(x.telephone(), x.text(), x.index());
509
insertFirstEmpty(x.telephone(), x.text());
512
void Phonebook::insert (iterator pos, long n, const PhonebookEntry& x)
515
for (long i = 0; i < n; i++)
516
if (x.useIndex() && x.index() != -1)
517
insert(x.telephone(), x.text(), x.index());
519
insertFirstEmpty(x.telephone(), x.text());
522
Phonebook::iterator Phonebook::erase(iterator position)
525
if (! position->empty())
527
position->set("", "");
533
Phonebook::iterator Phonebook::erase(iterator first, iterator last)
537
for (i = first; i != last; ++i)
542
void Phonebook::clear() throw(GsmException)
544
for (iterator i = begin(); i != end(); ++i)
548
Phonebook::iterator Phonebook::find(string text) throw(GsmException)
553
for (int i = 0; i < _maxSize; i++)
554
if (_phonebook[i].text() == text)
557
findEntry(text, index, telephone);
559
for (int i = 0; i < _maxSize; i++)
560
if (_phonebook[i].index() == index)
561
if (_phonebook[i].cached())
563
// if entry was already (= cached) and is now different
564
// the SIM card or it's contents were changed
565
if (_phonebook[i]._telephone != telephone ||
566
_phonebook[i]._text != text)
567
throw GsmException(_("SIM card changed while accessing phonebook"),
572
_phonebook[i]._cached = true;
573
_phonebook[i]._telephone = telephone;
574
_phonebook[i]._text = text;
580
Phonebook::~Phonebook()