~ubuntu-branches/ubuntu/breezy/koffice/breezy-security

« back to all changes in this revision

Viewing changes to lib/kospell/koaspell.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2005-10-11 14:49:50 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051011144950-lwpngbifzp8nk0ds
Tags: 1:1.4.1-0ubuntu7
* SECURITY UPDATE: fix heap based buffer overflow in the RTF importer of KWord
* Opening specially crafted RTF files in KWord can cause
  execution of abitrary code.
* Add kubuntu_01_rtfimport_heap_overflow.diff
* References:
  CAN-2005-2971
  CESA-2005-005
  http://www.koffice.org/security/advisory-20051011-1.txt

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* This file is part of the KDE libraries
2
 
   Copyright (C) 1997 David Sweet <dsweet@kde.org>
3
 
   Copyright (C) 2000-2001 Wolfram Diestel <wolfram@steloj.de>
4
 
   Copyright (C) 2002-2003 Laurent Montel <lmontel@mandrakesoft.com>
5
 
 
6
 
   This library is free software; you can redistribute it and/or
7
 
   modify it under the terms of the GNU Library General Public
8
 
   License version 2 as published by the Free Software Foundation.
9
 
 
10
 
   This library is distributed in the hope that it will be useful,
11
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 
   Library General Public License for more details.
14
 
 
15
 
   You should have received a copy of the GNU Library General Public License
16
 
   along with this library; see the file COPYING.LIB.  If not, write to
17
 
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18
 
   Boston, MA 02111-1307, USA.
19
 
*/
20
 
#ifdef HAVE_CONFIG_H
21
 
#include <config.h>
22
 
#endif
23
 
 
24
 
#ifdef HAVE_LIBASPELL
25
 
 
26
 
#include <stdio.h>
27
 
#include <sys/time.h>
28
 
#include <sys/types.h>
29
 
#include <unistd.h>
30
 
#include <ctype.h>
31
 
#include <stdlib.h> // atoi
32
 
 
33
 
#ifdef HAVE_STRINGS_H
34
 
#include <strings.h>
35
 
#endif
36
 
 
37
 
#include <qtextcodec.h>
38
 
#include <qtimer.h>
39
 
#include <kapplication.h>
40
 
#include <kdebug.h>
41
 
#include <klocale.h>
42
 
#include "koaspell.h"
43
 
#include "koaspell.moc"
44
 
#include "koSpell.h"
45
 
#include "koSpelldlg.h"
46
 
#include <kwin.h>
47
 
#include <kprocio.h>
48
 
 
49
 
#include <qtimer.h>
50
 
 
51
 
#include <aspell.h>
52
 
 
53
 
 
54
 
KOASpell::KOASpell( KOSpellConfig *_ksc )
55
 
    :KOSpell(_ksc)
56
 
{
57
 
    initSpell(_ksc);
58
 
    initConfig();
59
 
    QTimer::singleShot( 0, this, SLOT( slotEmitCheckerReady() ) );
60
 
}
61
 
 
62
 
//TODO FIXME !!!! KOSpellConfig not used.
63
 
void KOASpell::initSpell(KOSpellConfig *_ksc)
64
 
{
65
 
    Q_UNUSED( _ksc );
66
 
    m_bIgnoreUpperWords=false;
67
 
    m_bIgnoreTitleCase=false;
68
 
    autocorrect = false;
69
 
    autoDelete = false;
70
 
    modaldlg = false;
71
 
    speller = 0L;
72
 
    config = 0L;
73
 
    offset = 0;
74
 
    ksdlg=0;
75
 
    lastpos = -1;
76
 
 
77
 
    personaldict=FALSE;
78
 
    dlgresult=-1;
79
 
 
80
 
    caption=QString::null;
81
 
 
82
 
    parent=0L;
83
 
}
84
 
 
85
 
KOASpell::KOASpell (QWidget *_parent, const QString &_caption,
86
 
                KOSpellConfig *_ksc,
87
 
                bool _modal,  bool _autocorrect, KOSpellerType _type)
88
 
    :KOSpell(_parent,_caption,_ksc,_modal,_autocorrect, _type )
89
 
{
90
 
    initSpell(_ksc);
91
 
    autocorrect = _autocorrect;
92
 
    modaldlg = _modal;
93
 
    caption=_caption;
94
 
    parent=_parent;
95
 
 
96
 
    setUpDialog();
97
 
    QTimer::singleShot( 0, this, SLOT( slotEmitCheckerReady() ) );
98
 
}
99
 
 
100
 
void KOASpell::slotEmitCheckerReady()
101
 
{
102
 
    emit ready( this );
103
 
    emit spellCheckerReady();
104
 
}
105
 
 
106
 
bool KOASpell::initConfig(const QString & language)
107
 
{
108
 
    config = new_aspell_config();
109
 
    kdDebug(30006)<<" ksconfig->dictionary() :"<<ksconfig->dictionary()<<endl;
110
 
    Q_ASSERT( ksconfig->client() == KOS_CLIENT_ASPELL );
111
 
    aspell_config_replace(config, "lang", language.isEmpty() ? (ksconfig->dictionary().isEmpty() ? "en": ksconfig->dictionary().latin1()) : language.latin1() );
112
 
 
113
 
    kdDebug(30006)<<" ksconfig->dictionary() :"<<ksconfig->dictionary()<<endl;
114
 
 
115
 
    AspellCanHaveError * ret;
116
 
    ret = new_aspell_speller(config);
117
 
    if (aspell_error(ret) != 0) {
118
 
        kdDebug(30006)<<"Error :"<<aspell_error_message(ret)<<endl;
119
 
        delete_aspell_can_have_error(ret);
120
 
        return false;
121
 
    }
122
 
    /**
123
 
       My version of aspell uses the iso-8859-x syntax for those...
124
 
    switch (ksconfig->encoding())
125
 
    {
126
 
    case KOS_E_LATIN1:
127
 
        aspell_config_replace(config, "encoding", "latin1");
128
 
        break;
129
 
    case KOS_E_LATIN2:
130
 
        aspell_config_replace(config, "encoding", "latin2");
131
 
        break;
132
 
    case KOS_E_LATIN3:
133
 
        aspell_config_replace(config, "encoding", "latin3");
134
 
        break;
135
 
        // add the other charsets here
136
 
    case KOS_E_LATIN4:
137
 
    case KOS_E_LATIN5:
138
 
    case KOS_E_LATIN7:
139
 
    case KOS_E_LATIN8:
140
 
    case KOS_E_LATIN9:
141
 
    case KOS_E_LATIN13:
142
 
    case KOS_E_LATIN15:
143
 
        // will work, if this is the default charset in the dictionary
144
 
        kdError(750) << "charsets iso-8859-4 .. iso-8859-15 not supported yet" << endl;
145
 
        break;
146
 
    case KOS_E_UTF8:
147
 
        aspell_config_replace(config, "encoding", "utf8");
148
 
        break;
149
 
    case KOS_E_KOI8U:
150
 
        //todo
151
 
        break;
152
 
    }
153
 
    */
154
 
    aspell_config_replace(config, "ignore-case", ksconfig->ignoreCase()?"true" : "false" );
155
 
    aspell_config_replace(config, "ignore-accents", ksconfig->ignoreAccent()?"true" : "false" );
156
 
 
157
 
    ret = new_aspell_speller(config);
158
 
 
159
 
    delete_aspell_config(config);
160
 
 
161
 
    if (aspell_error(ret) != 0) {
162
 
        printf("Error: %s\n",aspell_error_message(ret));
163
 
        delete_aspell_can_have_error(ret);
164
 
        return false;
165
 
    }
166
 
    speller = to_aspell_speller(ret);
167
 
    config = aspell_speller_config(speller);
168
 
    return true;
169
 
}
170
 
 
171
 
void
172
 
KOASpell::setUpDialog ()
173
 
{
174
 
    if (ksdlg)
175
 
        return;
176
 
    bool ret = initConfig();
177
 
    if ( !ret )
178
 
        return;
179
 
 
180
 
    //Set up the dialog box
181
 
    ksdlg=new KOSpellDlg (parent, ksconfig,"dialog", KOSpellConfig::indexFromLanguageFileName( ksconfig->dictionary()),  modaldlg, autocorrect );
182
 
    ksdlg->setCaption (caption);
183
 
#ifdef Q_WS_X11 // FIXME(E): Implement for Qt/Embedded
184
 
    KWin::setIcons (ksdlg->winId(), kapp->icon(), kapp->miniIcon());
185
 
#endif
186
 
    if ( modaldlg )
187
 
        ksdlg->setFocus();
188
 
}
189
 
 
190
 
bool KOASpell::addPersonal (const QString & word)
191
 
{
192
 
    if( !speller)
193
 
        return false;
194
 
    //add to aspell internal.
195
 
    aspell_speller_add_to_personal(speller, word.latin1(), word.length());
196
 
    //save directly into personnal dictionary.
197
 
    writePersonalDictionary();
198
 
    return true;
199
 
}
200
 
 
201
 
bool KOASpell::writePersonalDictionary ()
202
 
{
203
 
    if( !speller)
204
 
        return false;
205
 
    aspell_speller_save_all_word_lists(speller);
206
 
    kdDebug(30006)<<"aspell_speller_error_message(speller) :"<<aspell_speller_error_message(speller)<<endl;
207
 
    return true;
208
 
}
209
 
 
210
 
bool KOASpell::ignore (const QString & /*word*/)
211
 
{
212
 
    //fixme !!!!!!!!!!!!!!!!
213
 
    return true;
214
 
}
215
 
 
216
 
 
217
 
QStringList KOASpell::resultCheckWord( const QString &_word )
218
 
{
219
 
    if (_word.isEmpty() || !speller)
220
 
        return QStringList();
221
 
    kdDebug(30006)<<" aspell_config_retrieve(config, lang) :"<<aspell_config_retrieve(config, "lang")<<endl;
222
 
    QStringList result;
223
 
    const AspellWordList *wl = aspell_speller_suggest(speller, _word.latin1(), -1);
224
 
    if (wl == 0) {
225
 
        kdDebug(30006)<<"Error: "<< aspell_speller_error_message(speller)<<endl;
226
 
    } else {
227
 
        AspellStringEnumeration * els = aspell_word_list_elements(wl);
228
 
        const char * word2;
229
 
        while ( (word2 = aspell_string_enumeration_next(els)) != 0) {
230
 
            result.append( word2 );
231
 
            kdDebug(30006)<<" word2 :"<<word2<<endl;
232
 
        }
233
 
    }
234
 
    return result;
235
 
}
236
 
 
237
 
bool KOASpell::spellWord( const QString &_word )
238
 
{
239
 
    QStringList lst =resultCheckWord( _word );
240
 
    if ( lst.isEmpty() && ((lastpos >= (int)origbuffer.length()-1)|| lastpos<0) )
241
 
    {
242
 
        //change m_status before to emit signal otherwise
243
 
        //kword + multiframe doesn't work
244
 
        m_status = Finished;
245
 
        emit done( origbuffer );
246
 
        return false;
247
 
    }
248
 
    if ( lst.contains( _word ))
249
 
      return false;
250
 
 
251
 
    dialog( _word, lst);
252
 
    return true;
253
 
}
254
 
 
255
 
void KOASpell::nextWord()
256
 
{
257
 
    QString word;
258
 
    lastpos++;
259
 
    bool haveAnNumber = false;
260
 
    do
261
 
    {
262
 
        int i =0;
263
 
        for ( i = lastpos; i<(int)origbuffer.length();i++)
264
 
        {
265
 
            QChar ch = origbuffer[i];
266
 
            if ( ch.isSpace() || ch.isPunct() )
267
 
                break;
268
 
            if ( ch.isNumber() )
269
 
                haveAnNumber = true;
270
 
            word.append(ch);
271
 
        }
272
 
        lastpos = i;
273
 
        if ( !word.isEmpty() )
274
 
            testIgnoreWord( word, haveAnNumber );
275
 
        else
276
 
            lastpos++;
277
 
    }
278
 
    while ( word.isEmpty() && (lastpos < (int)origbuffer.length()-1));
279
 
    if ( m_status != Finished && !spellWord( word ))
280
 
    {
281
 
        checkNextWord();
282
 
    }
283
 
}
284
 
 
285
 
void KOASpell::testIgnoreWord( QString & word, bool haveAnNumber )
286
 
{
287
 
    if ( !ksconfig->spellWordWithNumber() && haveAnNumber )
288
 
    {
289
 
        word ="";
290
 
        return;
291
 
    }
292
 
 
293
 
    if(m_bIgnoreTitleCase && word==word.upper())
294
 
    {
295
 
        word ="";
296
 
        return;
297
 
    }
298
 
 
299
 
    if(m_bIgnoreUpperWords && word[0]==word[0].upper())
300
 
    {
301
 
        QString text=word[0]+word.right(word.length()-1).lower();
302
 
        if(text==word)
303
 
        {
304
 
            word ="";
305
 
            return;
306
 
        }
307
 
    }
308
 
 
309
 
    //We don't take advantage of ispell's ignore function because
310
 
    //we can't interrupt ispell's output (when checking a large
311
 
    //buffer) to add a word to _it's_ ignore-list.
312
 
    if (!word.isEmpty() &&ignorelist.findIndex(word.lower())!=-1)
313
 
    {
314
 
        word ="";
315
 
        return;
316
 
    }
317
 
    //
318
 
    QStringList::Iterator it = replacelist.begin();
319
 
    for(;it != replacelist.end(); ++it, ++it) // Skip two entries at a time.
320
 
    {
321
 
        if (word == *it) // Word matches
322
 
        {
323
 
            QString origWord = *it;
324
 
            ++it;
325
 
            word = *it;   // Replace it with the next entry
326
 
            correctWord( origWord ,  word);
327
 
            word ="";
328
 
        }
329
 
    }
330
 
}
331
 
 
332
 
void KOASpell::correctWord( const QString & originalword, const QString & newword )
333
 
{
334
 
    emit corrected (originalword ,  newword, lastpos+offset-originalword.length());
335
 
    offset+=newword.length()-originalword.length();
336
 
    newbuffer.replace (lastpos+offset, newword.length(), newword );
337
 
}
338
 
 
339
 
void KOASpell::previousWord()
340
 
{
341
 
    QString word;
342
 
    lastpos--;
343
 
    bool haveAnNumber = false;
344
 
    do
345
 
    {
346
 
        int i =0;
347
 
        for ( i = lastpos; i>=0;--i)
348
 
        {
349
 
            QChar ch = origbuffer[i];
350
 
            if ( ch.isSpace() || ch.isPunct() )
351
 
            {
352
 
                lastpos--;
353
 
                break;
354
 
            }
355
 
            if ( ch.isNumber() )
356
 
                haveAnNumber = true;
357
 
            word.prepend(ch);
358
 
        }
359
 
        lastpos = i;
360
 
        if ( !word.isEmpty() )
361
 
            testIgnoreWord( word, haveAnNumber );
362
 
        else
363
 
            lastpos--;
364
 
    }
365
 
    while ( word.isEmpty() && (lastpos >= 0));
366
 
 
367
 
    if ( m_status != Finished && !spellWord( word ))
368
 
    {
369
 
        checkNextWord();
370
 
    }
371
 
 
372
 
}
373
 
 
374
 
bool KOASpell::check( const QString &_buffer, bool _usedialog )
375
 
{
376
 
    if( !ksdlg )
377
 
        return false;
378
 
    lastpos = -1;
379
 
    usedialog = _usedialog;
380
 
    origbuffer = _buffer;
381
 
    m_status = Starting;
382
 
    if ( ( totalpos = origbuffer.length() ) == 0 )
383
 
    {
384
 
        emit done(origbuffer);
385
 
        return FALSE;
386
 
    }
387
 
 
388
 
    // Torben: I corrected the \n\n problem directly in the
389
 
    //         origbuffer since I got errors otherwise
390
 
    if ( origbuffer.right(2) != "\n\n" )
391
 
    {
392
 
        if (origbuffer.at(origbuffer.length()-1)!='\n')
393
 
        {
394
 
            origbuffer+='\n';
395
 
            origbuffer+='\n'; //shouldn't these be removed at some point?
396
 
        }
397
 
        else
398
 
            origbuffer+='\n';
399
 
    }
400
 
 
401
 
    newbuffer=origbuffer;
402
 
    //lastpos is a position in newbuffer (it has offset in it)
403
 
    offset=lastlastline=lastline=0;
404
 
    lastpos = -1;
405
 
 
406
 
 
407
 
    // send first buffer line
408
 
    int i = origbuffer.find('\n', 0)+1;
409
 
    QString qs;
410
 
    qs=origbuffer.mid (0,i);
411
 
    lastline=i; //the character position, not a line number
412
 
    if (_usedialog)
413
 
        ksdlg->show();
414
 
    else
415
 
        ksdlg->hide();
416
 
 
417
 
    //check new word.
418
 
    checkNextWord();
419
 
    return TRUE;
420
 
}
421
 
 
422
 
void KOASpell::checkNextWord()
423
 
{
424
 
    if ( !ksdlg)
425
 
        return;
426
 
 
427
 
    if ( !ksdlg->previousWord() )
428
 
        nextWord();
429
 
    else
430
 
        previousWord();
431
 
}
432
 
 
433
 
void KOASpell::dialog(const QString & word, QStringList & sugg )
434
 
{
435
 
    if ( !ksdlg )
436
 
        return;
437
 
    dlgorigword=word;
438
 
 
439
 
    connect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int)));
440
 
    ksdlg->init (word, &sugg);
441
 
    if (!ksdlg->previousWord())
442
 
        misspellingWord (word, sugg, lastpos+offset-word.length());
443
 
    else
444
 
        misspellingWord (word, sugg, lastpos+offset+1);
445
 
 
446
 
    ksdlg->show();
447
 
}
448
 
 
449
 
void KOASpell::dialog2 (int result)
450
 
{
451
 
    if ( !ksdlg )
452
 
        return;
453
 
    QString qs;
454
 
    disconnect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int)));
455
 
    dlgresult=result;
456
 
    ksdlg->standby();
457
 
 
458
 
    dlgreplacement=ksdlg->replacement();
459
 
    bool testNextWord = true;
460
 
    QString _replacement;
461
 
    switch (dlgresult)
462
 
    {
463
 
    case KOS_IGNORE:
464
 
        emit ignoreword(dlgorigword);
465
 
        break;
466
 
    case KOS_IGNOREALL:
467
 
        // would be better to lower case only words with beginning cap
468
 
        ignorelist.prepend(dlgorigword.lower());
469
 
        emit ignoreall (dlgorigword);
470
 
        break;
471
 
    case KOS_ADD:
472
 
        addPersonal (dlgorigword);
473
 
        personaldict=TRUE;
474
 
        emit addword (dlgorigword);
475
 
        // adding to personal dict takes effect at the next line, not the current
476
 
        ignorelist.prepend(dlgorigword.lower());
477
 
        break;
478
 
    case KOS_REPLACEALL:
479
 
        replacelist.append (dlgorigword);
480
 
        _replacement = replacement();
481
 
        replacelist.append (_replacement);
482
 
 
483
 
        emit replaceall( dlgorigword ,  _replacement );
484
 
        correctWord( dlgorigword ,  _replacement );
485
 
        break;
486
 
    case KOS_ADDAUTOCORRECT:
487
 
        //todo add new word ????
488
 
        emit addAutoCorrect (dlgorigword , replacement());
489
 
    case KOS_REPLACE:
490
 
        correctWord( dlgorigword ,  replacement() );
491
 
        break;
492
 
    case KOS_CHECKAGAINWITHNEWLANGUAGE:
493
 
        changeSpellLanguage( ksdlg->languageIndex());
494
 
        spellCheckReplaceWord( dlgreplacement);
495
 
        testNextWord = false;
496
 
        break;
497
 
    case KOS_CHECKAGAIN:
498
 
        spellCheckReplaceWord( dlgreplacement);
499
 
        testNextWord = false;
500
 
        break;
501
 
    case KOS_STOP:
502
 
        testNextWord = false;
503
 
        ksdlg->hide();
504
 
        //buffer=newbuffer);
505
 
        emit done (newbuffer);
506
 
        emit death();
507
 
        break;
508
 
    case KOS_CANCEL:
509
 
        testNextWord = false;
510
 
        //      kdDebug(30006) << "cancelled\n" << endl;
511
 
        ksdlg->hide();
512
 
        emit done (origbuffer);
513
 
        emit death();
514
 
        break;
515
 
    }
516
 
    if ( testNextWord)
517
 
        checkNextWord();
518
 
}
519
 
 
520
 
void KOASpell::spellCheckReplaceWord( const QString & _word)
521
 
{
522
 
    if ( !ksdlg )
523
 
        return;
524
 
    connect (ksdlg, SIGNAL (command (int)), this, SLOT (dialog2(int)));
525
 
    QStringList lst;
526
 
    lst=resultCheckWord( _word );
527
 
    ksdlg->changeSuggList( &lst);
528
 
    ksdlg->show();
529
 
}
530
 
 
531
 
void KOASpell::deleteSpellChecker()
532
 
{
533
 
    if( speller )
534
 
    {
535
 
        delete_aspell_speller(speller);
536
 
        speller = 0;
537
 
    }
538
 
}
539
 
 
540
 
KOASpell::~KOASpell ()
541
 
{
542
 
    deleteSpellChecker();
543
 
}
544
 
 
545
 
 
546
 
void KOASpell::changeSpellLanguage( int index )
547
 
{
548
 
    deleteSpellChecker();
549
 
    initConfig( KOSpellConfig::listOfLanguageFileName()[index].latin1());
550
 
#if 0
551
 
    kdDebug(30006)<<"Before KOSpellConfig::listOfLanguageFileName()[index].latin1() :"<<KOSpellConfig::listOfLanguageFileName()[index].latin1()<<endl;
552
 
    aspell_config_replace(config, "lang",KOSpellConfig::listOfLanguageFileName()[index].latin1());
553
 
    kdDebug(30006)<<" After aspell_config_retrieve(config, lang) :"<<aspell_config_retrieve(config, "lang")<<endl;
554
 
#endif
555
 
}
556
 
 
557
 
 
558
 
int KOASpell::modalCheck( QString& text, KOSpellConfig* _kcs )
559
 
{
560
 
    modalreturn = 0;
561
 
    modaltext = text;
562
 
 
563
 
    KOASpell* m_spell = new KOASpell(0L, i18n("Spell Checker"), 0 ,_kcs,true );
564
 
    QObject::connect( m_spell, SIGNAL( death() ),
565
 
                      m_spell, SLOT( slotModalSpellCheckerFinished() ) );
566
 
    QObject::connect( m_spell, SIGNAL( corrected( const QString &, const QString &, unsigned int ) ),
567
 
                      m_spell, SLOT( slotSpellCheckerCorrected( const QString &, const QString &, unsigned int ) ) );
568
 
    QObject::connect( m_spell, SIGNAL( done( const QString & ) ),
569
 
                      m_spell, SLOT( slotModalDone( const QString & ) ) );
570
 
 
571
 
    bool result = m_spell->check( text );
572
 
    if ( !result)
573
 
    {
574
 
        delete m_spell;
575
 
        m_spell=0L;
576
 
        return modalreturn;
577
 
    }
578
 
 
579
 
    while (m_spell->status()!=Finished)
580
 
        kapp->processEvents();
581
 
 
582
 
    text = modaltext;
583
 
    delete m_spell;
584
 
    return modalreturn;
585
 
}
586
 
 
587
 
void KOASpell::slotSpellCheckerCorrected( const QString & oldText, const QString & newText, unsigned int pos )
588
 
{
589
 
    modaltext=modaltext.replace(pos,oldText.length(),newText);
590
 
}
591
 
 
592
 
 
593
 
void KOASpell::slotModalDone( const QString &/*_buffer*/ )
594
 
{
595
 
    slotModalSpellCheckerFinished();
596
 
}
597
 
 
598
 
void KOASpell::slotModalSpellCheckerFinished()
599
 
{
600
 
    modalreturn=(int)this->status();
601
 
}
602
 
 
603
 
 
604
 
#endif
605
 
 
606