~ubuntu-branches/ubuntu/trusty/gsmlib/trusty

« back to all changes in this revision

Viewing changes to gsmlib/gsm_phonebook.cc

  • Committer: Bazaar Package Importer
  • Author(s): Mikael Hedin
  • Date: 2002-01-24 12:59:07 UTC
  • Revision ID: james.westby@ubuntu.com-20020124125907-b7qkpokx5283jdpu
Tags: upstream-1.8
ImportĀ upstreamĀ versionĀ 1.8

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// *************************************************************************
 
2
// * GSM TA/ME library
 
3
// *
 
4
// * File:    gsm_phonebook.cc
 
5
// *
 
6
// * Purpose: Phonebook management functions
 
7
// *
 
8
// * Author:  Peter Hofmann (software@pxh.de)
 
9
// *
 
10
// * Created: 6.5.1999
 
11
// *************************************************************************
 
12
 
 
13
#ifdef HAVE_CONFIG_H
 
14
#include <gsm_config.h>
 
15
#endif
 
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>
 
21
#include <strstream>
 
22
#include <iostream>
 
23
#include <assert.h>
 
24
#include <ctype.h>
 
25
 
 
26
using namespace std;
 
27
using namespace gsmlib;
 
28
 
 
29
// PhonebookEntry members
 
30
 
 
31
PhonebookEntry::PhonebookEntry(const PhonebookEntryBase &e)
 
32
  throw(GsmException) : _cached(true), _myPhonebook(NULL)
 
33
{
 
34
  set(e.telephone(), e.text(), e.index(), e.useIndex());
 
35
}
 
36
 
 
37
void PhonebookEntry::set(string telephone, string text, int index,
 
38
                         bool useIndex)
 
39
  throw(GsmException)
 
40
{
 
41
  checkTextAndTelephone(text, telephone);
 
42
 
 
43
  if (_myPhonebook != NULL)
 
44
  {
 
45
    if (text.length() > _myPhonebook->getMaxTextLen())
 
46
      throw GsmException(
 
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()),
 
51
        ParameterError);
 
52
    
 
53
    if (telephone.length() > _myPhonebook->getMaxTelephoneLen())
 
54
      throw GsmException(
 
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()),
 
60
        ParameterError);
 
61
 
 
62
    _myPhonebook->writeEntry(_index, telephone, text);
 
63
  }
 
64
  else
 
65
    _index = index;
 
66
 
 
67
  _useIndex = useIndex;
 
68
  _cached = true;
 
69
  _telephone = telephone;
 
70
  _text = text;
 
71
  _changed = true;
 
72
}
 
73
 
 
74
string PhonebookEntry::text() const throw(GsmException)
 
75
{
 
76
  if (! cached())
 
77
  {
 
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;
 
83
  }
 
84
  return _text;
 
85
}
 
86
 
 
87
string PhonebookEntry::telephone() const throw(GsmException)
 
88
{
 
89
  if (! cached())
 
90
  {
 
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;
 
96
  }
 
97
  return _telephone;
 
98
}
 
99
 
 
100
bool PhonebookEntry::cached() const
 
101
{
 
102
  if (_myPhonebook == NULL)
 
103
    return _cached;
 
104
  else
 
105
    return _cached && _myPhonebook->_useCache;
 
106
}
 
107
 
 
108
PhonebookEntry::PhonebookEntry(const PhonebookEntry &e) throw(GsmException)
 
109
{
 
110
  set(e._telephone, e._text, e._index, e._useIndex);
 
111
}
 
112
 
 
113
PhonebookEntry &PhonebookEntry::operator=(const PhonebookEntry &e)
 
114
  throw(GsmException)
 
115
{
 
116
  set(e._telephone, e._text, e._index, e._useIndex);
 
117
  return *this;
 
118
}
 
119
 
 
120
// Phonebook members
 
121
 
 
122
int Phonebook::parsePhonebookEntry(string response,
 
123
                                   string &telephone, string &text)
 
124
{
 
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] != '"')
 
128
    response += '"';
 
129
  Parser p(response);
 
130
 
 
131
  int index = p.parseInt();
 
132
  p.parseComma();
 
133
 
 
134
  // handle case of empty entry
 
135
  if (p.getEol().substr(0, 5) == "EMPTY")
 
136
  {
 
137
    telephone = "";
 
138
    text = "";
 
139
    return index;
 
140
  }
 
141
 
 
142
  telephone = p.parseString();
 
143
  p.parseComma();
 
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;
 
149
  p.parseComma();
 
150
  text = p.parseString(false, true);
 
151
  if (lowercase(_myMeTa.getCurrentCharSet()) == "gsm")
 
152
    text = gsmToLatin1(text);
 
153
  if (numberFormat == InternationalNumberFormat)
 
154
  {
 
155
    // skip leading "+" signs that may already exist
 
156
    while (telephone.length() > 0 && telephone[0] == '+')
 
157
      telephone = telephone.substr(1);
 
158
    telephone = "+" + telephone;
 
159
  }
 
160
 
 
161
  return index;
 
162
}
 
163
 
 
164
void Phonebook::readEntry(int index, string &telephone, string &text)
 
165
  throw(GsmException)
 
166
{
 
167
  // select phonebook
 
168
  _myMeTa.setPhonebook(_phonebookName);
 
169
 
 
170
  // read entry
 
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)
 
176
 
 
177
  if (response.length() == 0)   // OK phone returned empty response
 
178
  {
 
179
    telephone = text = "";      // then the entry is empty as well
 
180
  }
 
181
  else
 
182
    parsePhonebookEntry(response, telephone, text);
 
183
 
 
184
#ifndef NDEBUG
 
185
  if (debugLevel() >= 1)
 
186
    cerr << "*** Reading PB entry " << index << " number " << telephone 
 
187
         << " text " << text << endl;
 
188
#endif
 
189
}
 
190
 
 
191
void Phonebook::findEntry(string text, int &index, string &telephone)
 
192
  throw(GsmException)
 
193
{
 
194
  // select phonebook
 
195
  _myMeTa.setPhonebook(_phonebookName);
 
196
 
 
197
  // read entry
 
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)
 
203
 
 
204
  if (response.length() == 0)   // OK phone returned empty response
 
205
  {
 
206
    telephone = "";      // then the entry is empty as well
 
207
    index = 0;
 
208
  }
 
209
  else
 
210
    index=parsePhonebookEntry(response, telephone, text);
 
211
 
 
212
#ifndef NDEBUG
 
213
  if (debugLevel() >= 1)
 
214
    cerr << "*** Finding PB entry " << text << " number " << telephone 
 
215
         << " index " << index << endl;
 
216
#endif
 
217
}
 
218
 
 
219
void Phonebook::writeEntry(int index, string telephone, string text)
 
220
  throw(GsmException)
 
221
{
 
222
#ifndef NDEBUG
 
223
  if (debugLevel() >= 1)
 
224
    cerr << "*** Writing PB entry #" << index << " number '" << telephone
 
225
         << "' text '" << text << "'" << endl;
 
226
#endif
 
227
  // select phonebook
 
228
  _myMeTa.setPhonebook(_phonebookName);
 
229
 
 
230
  // write entry
 
231
  string s;
 
232
  if (telephone == "" && text == "")
 
233
  {
 
234
    ostrstream os;
 
235
    os << "+CPBW=" << index;
 
236
    os << ends;
 
237
    char *ss = os.str();
 
238
    s = string(ss);
 
239
    delete[] ss;
 
240
  }
 
241
  else
 
242
  {
 
243
    int type;
 
244
    if (telephone.find('+') == string::npos)
 
245
      type = UnknownNumberFormat;
 
246
    else
 
247
      type = InternationalNumberFormat;
 
248
    string gsmText = text;
 
249
    if (lowercase(_myMeTa.getCurrentCharSet()) == "gsm")
 
250
      gsmText = latin1ToGsm(gsmText);
 
251
    ostrstream os;
 
252
    os << "+CPBW=" << index << ",\"" << telephone << "\"," << type
 
253
       << ",\"";
 
254
    os << ends;
 
255
    char *ss = os.str();
 
256
    s = string(ss);
 
257
    delete[] ss;
 
258
    // this cannot be added with ostrstream because the gsmText can
 
259
    // contain a zero (GSM default alphabet for '@')
 
260
    s +=  gsmText + "\"";
 
261
  }
 
262
  _at->chat(s);
 
263
}
 
264
 
 
265
Phonebook::iterator Phonebook::insertFirstEmpty(string telephone, string text)
 
266
  throw(GsmException)
 
267
{
 
268
  for (int i = 0; i < _maxSize; i++)
 
269
    if (_phonebook[i].empty())
 
270
    {
 
271
      _phonebook[i].set(telephone, text);
 
272
      adjustSize(1);
 
273
      return begin() + i;
 
274
    }
 
275
  throw GsmException(_("phonebook full"), OtherError);
 
276
}
 
277
 
 
278
Phonebook::iterator Phonebook::insert(const string telephone,
 
279
                                      const string text,
 
280
                                      const int index)
 
281
{
 
282
  for (int i = 0; i < _maxSize; i++)
 
283
    if (_phonebook[i].index() == index)
 
284
      if (_phonebook[i].empty())
 
285
      {
 
286
        _phonebook[i].set(telephone, text);
 
287
        adjustSize(1);
 
288
        return begin() + i;
 
289
      }
 
290
      else
 
291
        throw GsmException(_("attempt to overwrite phonebook entry"),
 
292
                           OtherError);
 
293
  return end();
 
294
}
 
295
 
 
296
Phonebook::Phonebook(string phonebookName, Ref<GsmAt> at, MeTa &myMeTa,
 
297
                     bool preload) throw(GsmException) :
 
298
  _phonebookName(phonebookName), _at(at), _myMeTa(myMeTa), _useCache(true)
 
299
{
 
300
  // select phonebook
 
301
  _myMeTa.setPhonebook(_phonebookName);
 
302
 
 
303
  // query size and maximum capacity of phonebook
 
304
  _size = -1;                   // -1 means not known yet
 
305
  _maxSize = -1;
 
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();
 
311
    q.parseComma();
 
312
    _maxSize = q.parseInt();
 
313
  }
 
314
  
 
315
  // get basic phonebook info from ME
 
316
  Parser p(_at->chat("+CPBR=?", "+CPBR:"));
 
317
 
 
318
  // get index of actually available entries in the phonebook
 
319
  vector<bool> availablePositions = p.parseIntList();
 
320
  p.parseComma();
 
321
  _maxNumberLength = p.parseInt();
 
322
  p.parseComma();
 
323
  _maxTextLength = p.parseInt();
 
324
  
 
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.
 
333
  if (_maxSize == -1)
 
334
  {
 
335
    _maxSize = 0;
 
336
    for (vector<bool>::iterator i = availablePositions.begin();
 
337
         i != availablePositions.end(); ++i)
 
338
      if (*i) ++_maxSize;
 
339
  }
 
340
 
 
341
  // for use with preload below
 
342
  int *meToPhonebookIndexMap =
 
343
    (int*)alloca(sizeof(int) * (availablePositions.size() + 1));
 
344
 
 
345
  // initialize phone book entries
 
346
  if (_maxSize == 0)
 
347
    _phonebook = NULL;
 
348
  else
 
349
    _phonebook = new PhonebookEntry[_maxSize];
 
350
  int nextAvailableIndex = 0;
 
351
  for (int i = 0; i < _maxSize; i++)
 
352
  {
 
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;
 
359
  }
 
360
 
 
361
  // find out first index number of phonebook
 
362
  int firstIndex;
 
363
  for (int i = 0; i < _maxSize; i++)
 
364
    if (availablePositions[i])
 
365
    {
 
366
      firstIndex = i;
 
367
      break;
 
368
    }
 
369
 
 
370
  // preload phonebook
 
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)
 
377
  {
 
378
    int entriesRead = 0;
 
379
    int startIndex = firstIndex;
 
380
 
 
381
    while (entriesRead < _size)
 
382
    {
 
383
      reportProgress(0, _maxSize); // chatv also calls reportProgress()
 
384
      vector<string> responses =
 
385
        _at->chatv("+CPBR=" + intToStr(startIndex) +
 
386
                   "," + intToStr(_maxSize + firstIndex - 1),
 
387
                   "+CPBR:", true);
 
388
 
 
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)
 
394
      {
 
395
#ifndef NDEBUG
 
396
        if (debugLevel() >= 1)
 
397
          cerr << "*** error when preloading phonebook: "
 
398
            "not all entries returned" << endl;
 
399
#endif
 
400
        break;
 
401
      }
 
402
 
 
403
      for (vector<string>::iterator i = responses.begin();
 
404
           i != responses.end(); ++i)
 
405
      {
 
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);
 
412
 
 
413
        ++entriesRead;
 
414
        startIndex = meIndex + 1;
 
415
#ifndef NDEBUG
 
416
        if (debugLevel() >= 1)
 
417
          cerr << "*** Preloading PB entry " << meIndex
 
418
               << " number " << telephone 
 
419
               << " text " << text << endl;
 
420
#endif
 
421
      }
 
422
    }
 
423
  }
 
424
}
 
425
 
 
426
Phonebook::iterator Phonebook::begin()
 
427
{
 
428
  return &_phonebook[0];
 
429
}
 
430
 
 
431
Phonebook::const_iterator Phonebook::begin() const
 
432
{
 
433
  return &_phonebook[0];
 
434
}
 
435
 
 
436
Phonebook::iterator Phonebook::end()
 
437
{
 
438
  return &_phonebook[_maxSize];
 
439
}
 
440
 
 
441
Phonebook::const_iterator Phonebook::end() const
 
442
{
 
443
  return &_phonebook[_maxSize];
 
444
}
 
445
 
 
446
Phonebook::reference Phonebook::operator[](int n)
 
447
{
 
448
  return _phonebook[n];
 
449
}
 
450
 
 
451
Phonebook::const_reference Phonebook::operator[](int n) const
 
452
{
 
453
  return _phonebook[n];
 
454
}
 
455
 
 
456
Phonebook::reference Phonebook::front()
 
457
{
 
458
  return _phonebook[0];
 
459
}
 
460
 
 
461
Phonebook::const_reference Phonebook::front() const
 
462
{
 
463
  return _phonebook[0];
 
464
}
 
465
 
 
466
Phonebook::reference Phonebook::back()
 
467
{
 
468
  return _phonebook[_maxSize - 1];
 
469
}
 
470
 
 
471
Phonebook::const_reference Phonebook::back() const
 
472
{
 
473
  return _phonebook[_maxSize - 1];
 
474
}
 
475
 
 
476
int Phonebook::size() const throw(GsmException)
 
477
{
 
478
  if (_size != -1)
 
479
    return _size;
 
480
  else
 
481
  {
 
482
    int result = 0;
 
483
    for (int i = 0; i < _maxSize; i++)
 
484
      if (! _phonebook[i].empty())
 
485
        result++;
 
486
    Phonebook *thisPhonebook = const_cast<Phonebook*>(this);
 
487
    thisPhonebook->_size = result;
 
488
    return result;
 
489
  }
 
490
}
 
491
 
 
492
Phonebook::iterator Phonebook::insert(iterator position,
 
493
                                      const PhonebookEntry& x)
 
494
  throw(GsmException)
 
495
{
 
496
  if (x.useIndex() && x.index() != -1)
 
497
    return insert(x.telephone(), x.text(), x.index());
 
498
  else
 
499
    return insertFirstEmpty(x.telephone(), x.text());
 
500
}
 
501
 
 
502
void Phonebook::insert (iterator pos, int n, const PhonebookEntry& x)
 
503
  throw(GsmException)
 
504
{
 
505
  for (int i = 0; i < n; i++)
 
506
    if (x.useIndex() && x.index() != -1)
 
507
      insert(x.telephone(), x.text(), x.index());
 
508
    else
 
509
      insertFirstEmpty(x.telephone(), x.text());
 
510
}
 
511
 
 
512
void Phonebook::insert (iterator pos, long n, const PhonebookEntry& x)
 
513
  throw(GsmException)
 
514
{
 
515
  for (long i = 0; i < n; i++)
 
516
    if (x.useIndex() && x.index() != -1)
 
517
      insert(x.telephone(), x.text(), x.index());
 
518
    else
 
519
      insertFirstEmpty(x.telephone(), x.text());
 
520
}
 
521
 
 
522
Phonebook::iterator Phonebook::erase(iterator position)
 
523
  throw(GsmException)
 
524
{
 
525
  if (! position->empty())
 
526
  {
 
527
    position->set("", "");
 
528
    adjustSize(-1);
 
529
  }
 
530
  return position + 1;
 
531
}
 
532
 
 
533
Phonebook::iterator Phonebook::erase(iterator first, iterator last)
 
534
  throw(GsmException)
 
535
{
 
536
  iterator i;
 
537
  for (i = first; i != last; ++i)
 
538
    erase(i);
 
539
  return i;
 
540
}
 
541
 
 
542
void Phonebook::clear() throw(GsmException)
 
543
{
 
544
  for (iterator i = begin(); i != end(); ++i)
 
545
    erase(i);
 
546
}
 
547
 
 
548
Phonebook::iterator Phonebook::find(string text) throw(GsmException)
 
549
{
 
550
  int index;
 
551
  string telephone;
 
552
 
 
553
  for (int i = 0; i < _maxSize; i++)
 
554
    if (_phonebook[i].text() == text)
 
555
      return begin() + i;
 
556
 
 
557
  findEntry(text, index, telephone);
 
558
  
 
559
  for (int i = 0; i < _maxSize; i++)
 
560
    if (_phonebook[i].index() == index)
 
561
      if (_phonebook[i].cached())
 
562
      {
 
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"),
 
568
                             OtherError);
 
569
      }
 
570
      else
 
571
      {
 
572
        _phonebook[i]._cached = true;
 
573
        _phonebook[i]._telephone = telephone;
 
574
        _phonebook[i]._text = text;
 
575
        return begin() + i;
 
576
      }
 
577
  return end();
 
578
}
 
579
 
 
580
Phonebook::~Phonebook()
 
581
{
 
582
  delete []_phonebook;
 
583
}