summaryrefslogtreecommitdiffstats
path: root/kmailcvt/filter_pmail.cxx
blob: 982d713a9c2468214b1f0c9fc39259048c93b81e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
/***************************************************************************
                          FilterPMail.cxx  -  Pegasus-Mail import
                             -------------------
    begin                : Sat Jan 6 2001
    copyright            : (C) 2001 by Holger Schurig
                           (C) 2005 by Danny Kukawka
    email                : holgerschurig@gmx.de
                           danny.kukawka@web.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <config.h>
#include <klocale.h>
#include <kfiledialog.h>
#include <tqregexp.h>
#include <ktempfile.h>
#include <kdebug.h>

#include "filter_pmail.hxx"


FilterPMail::FilterPMail() :
        Filter(i18n("Import Folders From Pegasus-Mail"),
               "Holger Schurig <br>( rewritten by Danny Kukawka )",
               i18n("<p>Select the Pegasus-Mail directory on your system (containing *.CNM, *.PMM and *.MBX files). "
                    "On many systems this is stored in C:\\pmail\\mail or C:\\pmail\\mail\\admin</p>"
                    "<p><b>Note:</b> Since it is possible to recreate the folder structure, the folders "
                    "will be stored under: \"PegasusMail-Import\".</p>"))
{}

FilterPMail::~FilterPMail()
{
}

void FilterPMail::import(FilterInfo *info)
{
    inf = info;
    
    // Select directory from where I have to import files
    KFileDialog *kfd;
    kfd = new KFileDialog( TQDir::homeDirPath(), "", 0, "kfiledialog", true );
    kfd->setMode(KFile::Directory | KFile::LocalOnly);
    kfd->exec();
    chosenDir = kfd->selectedFile();
    delete kfd;

    if (chosenDir.isEmpty()) {
        info->alert(i18n("No directory selected."));
        return;
    }

    // Count total number of files to be processed
    info->addLog(i18n("Counting files..."));
    dir.setPath (chosenDir);
    TQStringList files = dir.entryList("*.[cC][nN][mM]; *.[pP][mM][mM]; *.[mM][bB][xX]", TQDir::Files, TQDir::Name);
    totalFiles = files.count();
    currentFile = 0;
    kdDebug() << "Count is " << totalFiles << endl;
    
    if(!(folderParsed = parseFolderMatrix())) {
        info->addLog(i18n("Cannot parse the folder structure; continuing import without subfolder support."));
    }

    info->addLog(i18n("Importing new mail files ('.cnm')..."));
    processFiles("*.[cC][nN][mM]", &FilterPMail::importNewMessage);
    info->addLog(i18n("Importing mail folders ('.pmm')..."));
    processFiles("*.[pP][mM][mM]", &FilterPMail::importMailFolder);
    info->addLog(i18n("Importing 'UNIX' mail folders ('.mbx')..."));
    processFiles("*.[mM][bB][xX]", &FilterPMail::importUnixMailFolder);

    info->addLog( i18n("Finished importing emails from %1").tqarg( chosenDir ));
    info->setCurrent(100);
    info->setOverall(100);
}

/** this looks for all files with the filemask 'mask' and calls the 'workFunc' on each of them */
void FilterPMail::processFiles(const TQString& mask, void(FilterPMail::* workFunc)(const TQString&) )
{
    if (inf->shouldTerminate()) return;
    
    TQStringList files = dir.entryList(mask, TQDir::Files, TQDir::Name);
    //kdDebug() << "Mask is " << mask << " count is " << files.count() << endl;
    for ( TQStringList::Iterator mailFile = files.begin(); mailFile != files.end(); ++mailFile ) {
        // Notify current file
        TQFileInfo mailfileinfo(*mailFile);
        inf->setFrom(mailfileinfo.fileName());

        // Clear the other fields
        inf->setTo(TQString());
        inf->setCurrent(TQString());
        inf->setCurrent(-1);

        // call worker function, increase progressbar
        (this->*workFunc)(dir.filePath(*mailFile));
        ++currentFile;
        inf->setOverall( (int) ((float) currentFile / totalFiles * 100));
        inf->setCurrent( 100 );
        if (inf->shouldTerminate()) return;
    }
}


/** this function imports one *.CNM message */
void FilterPMail::importNewMessage(const TQString& file)
{
    TQString destFolder("PegasusMail-Import/New Messages");
    inf->setTo(destFolder);

    /* comment by Danny Kukawka:
     * addMessage() == old function, need more time and check for duplicates
     * addMessage_fastImport == new function, faster and no check for duplicates
     */
    if(inf->removeDupMsg)
        addMessage( inf, destFolder, file );
    else
        addMessage_fastImport( inf, destFolder, file );
}


/** this function imports one mail folder file (*.PMM) */
void FilterPMail::importMailFolder(const TQString& file)
{
    // Format of a PMM file:
    // First comes a header with 128 bytes. At the beginning is the name of
    // the folder. Then there are some unknown bytes (strings). At offset 128
    // the first message starts.
    //
    // Each message is terminated by a 0x1A byte. The next message follows
    // immediately.
    //
    // The last message is followed by a 0x1A, too.
    //
    // 000000 6d 61 69 6c 73 65 72 76 65 72 2d 70 72 6f 6a 65    mailserver-proje
    // 000010 63 74 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ct..............
    // 000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
    // 000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
    // 000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
    // 000050 00 00 00 00 00 00 36 30 34 37 35 37 32 45 3a 36    ......6047572E:6
    // 000060 46 34 39 3a 46 4f 4c 30 31 33 35 35 00 00 00 00    F49:FOL01355....
    // 000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
    // 000080 52 65 74 75 72 6e 2d 50 61 74 68 3a 20 3c 75 72    Return-Path: <ur
    // ...
    // 000cb0 2d 2d 2d 2d 2d 2d 2d 2d 2d 2b 0d 0a 1a 52 65 74    ---------+...Ret
    // 000cc0 75 72 6e 2d 50 61 74 68 3a 20 3c 62 6f 75 6e 63    urn-Path: <bounc
    // ...
    // 04dc50 46 30 33 38 44 2e 36 31 35 44 37 34 44 30 2d 2d    F038D.615D74D0--
    // 04dc60 0d 0a 1a

    struct {
        char folder[86];
        char id[42];
    } pmm_head;
    
    long l = 0;
    TQFile f(file);
    if (!f.open(IO_ReadOnly)) {
        inf->alert(i18n("Unable to open %1, skipping").tqarg(file));
    } else {
        // Get folder name
        l = f.readBlock((char *) &pmm_head, sizeof(pmm_head));
        TQString folder("PegasusMail-Import/");
        if(folderParsed) 
            folder.append(getFolderName((TQString)pmm_head.id));
        else 
            folder.append(pmm_head.folder);
        inf->setTo(folder);
        inf->addLog(i18n("Importing %1").tqarg("../" + TQString(pmm_head.folder)));
        
        TQByteArray input(MAX_LINE);
        bool first_msg = true;
        
        while (!f.atEnd()) {
            KTempFile tempfile;
            inf->setCurrent( (int) ( ( (float) f.at() / f.size() ) * 100 ) );
            
            if(!first_msg) {
                // set the filepos back to last line minus the seperate char (0x1a)
                f.at(f.at() - l + 1); 
            }
            
            // no problem to loose the last line in file. This only contains a seperate char
            while ( ! f.atEnd() &&  (l = f.readLine(input.data(),MAX_LINE))) {
                    if (inf->shouldTerminate()){
                        tempfile.close();
                        tempfile.unlink();
                        return;
                    }
                    if(input[0] == 0x1a ) {
                        break;
                    } else {
                        tempfile.file()->writeBlock( input, l );
                    }
            }
            tempfile.close();
            
            if(inf->removeDupMsg)
                addMessage( inf, folder, tempfile.name() );
            else
                addMessage_fastImport( inf, folder, tempfile.name() );
            
            first_msg = false;
            tempfile.unlink();
        }
    }
    f.close();
}


/** imports a 'unix' format mail folder (*.MBX) */
void FilterPMail::importUnixMailFolder(const TQString& file)
{
    struct {
        char folder[58];
        char id[31];
    } pmg_head;
    
    TQFile f;
    TQString folder("PegasusMail-Import/"), s(file), seperate;
    TQByteArray line(MAX_LINE);
    int n = 0, l = 0;

    /** Get the folder name */
    s.replace( TQRegExp("mbx$"), "pmg");
    s.replace( TQRegExp("MBX$"), "PMG");
    f.setName(s);
    if (! f.open( IO_ReadOnly ) ) {
        inf->alert( i18n("Unable to open %1, skipping").tqarg( s ) );
        return;
    } else {
        f.readBlock((char *) &pmg_head, sizeof(pmg_head));
        f.close();
        
         if(folderParsed) 
            folder.append(getFolderName((TQString)pmg_head.id));
        else 
            folder.append(pmg_head.folder);
        
        inf->setTo(folder);
        inf->setTo(folder);
    }
    
    /** Read in the mbox */
    f.setName(file);
    if (! f.open( IO_ReadOnly ) ) {
        inf->alert( i18n("Unable to open %1, skipping").tqarg( s ) );
    } else {
        inf->addLog(i18n("Importing %1").tqarg("../" + TQString(pmg_head.folder)));
        l = f.readLine( line.data(),MAX_LINE); // read the first line which is unneeded
        while ( ! f.atEnd() ) {
            KTempFile tempfile;
            
            // we lost the last line, which is the first line of the new message in
            // this lopp, but this is ok, because this is the seperate line with
            // "From ???@???" and we can forget them
            while ( ! f.atEnd() &&  (l = f.readLine(line.data(),MAX_LINE)) && ((seperate = line.data()).left(5) != "From ")) {
                tempfile.file()->writeBlock(line.data(), l);
                if (inf->shouldTerminate()){
                    tempfile.close();
                    tempfile.unlink();
                    return;
                }
            }
            tempfile.close();
            if(inf->removeDupMsg)
                addMessage( inf, folder, tempfile.name() );
            else
                addMessage_fastImport( inf, folder, tempfile.name() );
            tempfile.unlink();   
            
            n++;
            inf->setCurrent(i18n("Message %1").tqarg(n));            
            inf->setCurrent( (int) ( ( (float) f.at() / f.size() ) * 100 ) );
        }
    }    
    f.close();
}

/** Parse the information about folderstructure to folderMatrix */
bool FilterPMail::parseFolderMatrix() 
{
    kdDebug() << "Start parsing the foldermatrix." << endl;
    inf->addLog(i18n("Parsing the folder structure..."));
    
    TQFile hierarch(chosenDir + "/hierarch.pm");
    if (! hierarch.open( IO_ReadOnly ) ) {
        inf->alert( i18n("Unable to open %1, skipping").tqarg( chosenDir + "hierarch.pm" ) );
        return false;
    } else {
        TQStringList tmpList;
        TQString tmpRead;
        while ( !hierarch.atEnd() &&  hierarch.readLine(tmpRead,100)) {
            TQString tmpArray[5];
            tmpRead.remove(tmpRead.length() -2,2);
            TQStringList tmpList = TQStringList::split(",", tmpRead, false);
            int i = 0;
            for ( TQStringList::Iterator it = tmpList.begin(); it != tmpList.end(); ++it, i++) {
                TQString _tmp = *it;
                if(i < 5) tmpArray[i] = _tmp.remove("\"");
                else {
                    hierarch.close();
                    return false; 
                }
            } 
            folderMatrix.append(tmpArray);
        }
    }
    hierarch.close();
    return true;
}

/** get the foldername for a given file ID from folderMatrix */
TQString FilterPMail::getFolderName(TQString ID) 
{
    bool found = false;
    TQString folder;
    TQString search = ID;
    
    while (!found)
    {
        for ( FolderStructureIterator it = folderMatrix.begin(); it != folderMatrix.end(); it++) {
            FolderStructure tmp = *it;
            
            TQString _ID = tmp[2];
            if(_ID == search) {
                TQString _type = tmp[0] + tmp[1];
                if(( _type == "21")) {
                    found = true;
                    break;
                }
                else {
                    folder.prepend((tmp[4] + "/"));
                    search = tmp[3];
                }
            }
        }  
    }
    return folder;
}