~ubuntu-branches/ubuntu/oneiric/kdepim/oneiric-updates

« back to all changes in this revision

Viewing changes to messageviewer/util.cpp

  • Committer: Package Import Robot
  • Author(s): Philip Muškovac
  • Date: 2011-06-28 19:33:24 UTC
  • mfrom: (0.2.13) (0.1.13 sid)
  • Revision ID: package-import@ubuntu.com-20110628193324-8yvjs8sdv9rdoo6c
Tags: 4:4.7.0-0ubuntu1
* New upstream release
  - update install files
  - add missing kdepim-doc package to control file
  - Fix Vcs lines
  - kontact breaks/replaces korganizer << 4:4.6.80
  - tighten the dependency of kdepim-dev on libkdepim4 to fix lintian error

Show diffs side-by-side

added added

removed removed

Lines of Context:
35
35
**   your version.
36
36
**
37
37
*******************************************************************************/
 
38
#include <config-messageviewer.h>
38
39
 
39
40
#include "util.h"
40
41
 
 
42
#include "iconnamecache.h"
 
43
#include "nodehelper.h"
 
44
 
 
45
#include "messagecore/globalsettings.h"
 
46
#include "messagecore/nodehelper.h"
 
47
#include "messagecore/stringutil.h"
 
48
 
 
49
#include <KMime/Message>
 
50
 
41
51
#include <kcharsets.h>
 
52
#include <KFileDialog>
42
53
#include <kglobal.h>
43
54
#include <klocale.h>
44
55
#include <kmessagebox.h>
45
56
#include <kio/netaccess.h>
46
57
#include <kdebug.h>
 
58
#include <KMimeType>
 
59
#include <KTemporaryFile>
47
60
 
48
61
#include <QTextCodec>
49
62
#include <QWidget>
50
63
 
 
64
using namespace MessageViewer;
 
65
 
51
66
bool Util::checkOverwrite( const KUrl &url, QWidget *w )
52
67
{
53
68
  if ( KIO::NetAccess::exists( url, KIO::NetAccess::DestinationSide, w ) ) {
62
77
  return true;
63
78
}
64
79
 
65
 
#ifdef Q_WS_MACX
 
80
QString Util::fileNameForMimetype( const QString &mimeType, int iconSize,
 
81
                                   const QString &fallbackFileName1,
 
82
                                   const QString &fallbackFileName2 )
 
83
{
 
84
  QString fileName;
 
85
  KMimeType::Ptr mime = KMimeType::mimeType( mimeType, KMimeType::ResolveAliases );
 
86
  if ( mime ) {
 
87
    fileName = mime->iconName();
 
88
  } else {
 
89
    kWarning() << "unknown mimetype" << mimeType;
 
90
  }
 
91
 
 
92
  if ( fileName.isEmpty() )
 
93
  {
 
94
    fileName = fallbackFileName1;
 
95
    if ( fileName.isEmpty() )
 
96
      fileName = fallbackFileName2;
 
97
    if ( !fileName.isEmpty() ) {
 
98
      fileName = KMimeType::findByPath( "/tmp/" + fileName, 0, true )->iconName();
 
99
    }
 
100
  }
 
101
 
 
102
  return IconNameCache::instance()->iconPath( fileName, iconSize );
 
103
}
 
104
 
 
105
#if defined Q_WS_WIN || defined Q_WS_MACX
66
106
#include <QDesktopServices>
67
107
#endif
68
108
 
69
 
bool Util::handleUrlOnMac( const KUrl& url )
 
109
bool Util::handleUrlWithQDesktopServices( const KUrl& url )
70
110
{
71
 
#ifdef Q_WS_MACX
 
111
#if defined Q_WS_WIN || defined Q_WS_MACX
72
112
  QDesktopServices::openUrl( url );
73
113
  return true;
74
114
#else
77
117
#endif
78
118
}
79
119
 
80
 
QStringList Util::supportedEncodings(bool usAscii)
81
 
{
82
 
  // cberzan: replaced by KCodecAction in CodecManager
83
 
  QStringList encodingNames = KGlobal::charsets()->availableEncodingNames();
84
 
  QStringList encodings;
85
 
  QMap<QString,bool> mimeNames;
86
 
  for (QStringList::Iterator it = encodingNames.begin();
87
 
    it != encodingNames.end(); ++it)
88
 
  {
89
 
    QTextCodec *codec = KGlobal::charsets()->codecForName(*it);
90
 
//     kDebug() << "name" << *it << "codec" << codec << "name" << (codec ? codec->name() : "NULL");
91
 
    QString mimeName = (codec) ? QString(codec->name()).toLower() : (*it);
92
 
    if (!mimeNames.contains(mimeName) )
93
 
    {
94
 
      encodings.append( KGlobal::charsets()->descriptionForEncoding(*it) );
95
 
      mimeNames.insert( mimeName, true );
96
 
//       kDebug() << "added" << mimeName;
97
 
    }
98
 
  }
99
 
  encodings.sort();
100
 
  if (usAscii)
101
 
    encodings.prepend(KGlobal::charsets()->descriptionForEncoding("us-ascii") );
102
 
  return encodings;
103
 
}
104
 
 
105
 
//-----------------------------------------------------------------------------
106
 
QString Util::fixEncoding( const QString &encoding )
107
 
{
108
 
  QString returnEncoding = encoding;
109
 
  // According to http://www.iana.org/assignments/character-sets, uppercase is
110
 
  // preferred in MIME headers
111
 
  if ( returnEncoding.toUpper().contains( "ISO " ) ) {
112
 
    returnEncoding = returnEncoding.toUpper();
113
 
    returnEncoding.replace( "ISO ", "ISO-" );
114
 
  }
115
 
  return returnEncoding;
116
 
}
117
 
 
118
 
//-----------------------------------------------------------------------------
119
 
QString Util::encodingForName( const QString &descriptiveName )
120
 
{
121
 
  QString encoding = KGlobal::charsets()->encodingForName( descriptiveName );
122
 
  return Util::fixEncoding( encoding );
123
 
}
 
120
QList<KMime::Content*> Util::allContents( const KMime::Content *message )
 
121
{
 
122
  KMime::Content::List result;
 
123
  KMime::Content *child = MessageCore::NodeHelper::firstChild( message );
 
124
  if ( child ) {
 
125
    result += child;
 
126
    result += allContents( child );
 
127
  }
 
128
  KMime::Content *next = MessageCore::NodeHelper::nextSibling( message );
 
129
  if ( next ) {
 
130
    result += next;
 
131
    result += allContents( next );
 
132
  }
 
133
 
 
134
  return result;
 
135
}
 
136
 
 
137
QList<KMime::Content*> Util::extractAttachments( const KMime::Message *message )
 
138
{
 
139
  KMime::Content::List contents = allContents( message );
 
140
  for ( KMime::Content::List::iterator it = contents.begin();
 
141
        it != contents.end(); ) {
 
142
    // only body parts which have a filename or a name parameter (except for
 
143
    // the root node for which name is set to the message's subject) are
 
144
    // considered attachments
 
145
    KMime::Content* content = *it;
 
146
    if ( content->contentDisposition()->filename().trimmed().isEmpty() &&
 
147
          ( content->contentType()->name().trimmed().isEmpty() ||
 
148
            content == message ) ) {
 
149
      KMime::Content::List::iterator delIt = it;
 
150
      ++it;
 
151
      contents.erase( delIt );
 
152
    } else {
 
153
      ++it;
 
154
    }
 
155
  }
 
156
  return contents;
 
157
}
 
158
 
 
159
bool Util::saveContents( QWidget *parent, const QList<KMime::Content*> &contents )
 
160
{
 
161
  KUrl url, dirUrl;
 
162
  if ( contents.count() > 1 ) {
 
163
    // get the dir
 
164
    dirUrl = KFileDialog::getExistingDirectoryUrl( KUrl( "kfiledialog:///saveAttachment" ),
 
165
                                                   parent,
 
166
                                                   i18n( "Save Attachments To" ) );
 
167
    if ( !dirUrl.isValid() ) {
 
168
      return false;
 
169
    }
 
170
 
 
171
    // we may not get a slash-terminated url out of KFileDialog
 
172
    dirUrl.adjustPath( KUrl::AddTrailingSlash );
 
173
  }
 
174
  else {
 
175
    // only one item, get the desired filename
 
176
    KMime::Content *content = contents.first();
 
177
    QString fileName = NodeHelper::fileName( content );
 
178
    fileName = MessageCore::StringUtil::cleanFileName( fileName );
 
179
    if ( fileName.isEmpty() ) {
 
180
      fileName = i18nc( "filename for an unnamed attachment", "attachment.1" );
 
181
    }
 
182
    url = KFileDialog::getSaveUrl( KUrl( "kfiledialog:///saveAttachment/" + fileName ),
 
183
                                   QString(),
 
184
                                   parent,
 
185
                                   i18n( "Save Attachment" ) );
 
186
    if ( url.isEmpty() ) {
 
187
      return false;
 
188
    }
 
189
  }
 
190
 
 
191
  QMap< QString, int > renameNumbering;
 
192
 
 
193
  bool globalResult = true;
 
194
  int unnamedAtmCount = 0;
 
195
  bool overwriteAll = false;
 
196
  foreach( KMime::Content *content, contents ) {
 
197
    KUrl curUrl;
 
198
    if ( !dirUrl.isEmpty() ) {
 
199
      curUrl = dirUrl;
 
200
      QString fileName = MessageViewer::NodeHelper::fileName( content );
 
201
      fileName = MessageCore::StringUtil::cleanFileName( fileName );
 
202
      if ( fileName.isEmpty() ) {
 
203
        ++unnamedAtmCount;
 
204
        fileName = i18nc( "filename for the %1-th unnamed attachment",
 
205
                          "attachment.%1", unnamedAtmCount );
 
206
      }
 
207
      curUrl.setFileName( fileName );
 
208
    } else {
 
209
      curUrl = url;
 
210
    }
 
211
 
 
212
    if ( !curUrl.isEmpty() ) {
 
213
 
 
214
      // Rename the file if we have already saved one with the same name:
 
215
      // try appending a number before extension (e.g. "pic.jpg" => "pic_2.jpg")
 
216
      QString origFile = curUrl.fileName();
 
217
      QString file = origFile;
 
218
 
 
219
      while ( renameNumbering.contains(file) ) {
 
220
        file = origFile;
 
221
        int num = renameNumbering[file] + 1;
 
222
        int dotIdx = file.lastIndexOf('.');
 
223
        file = file.insert( (dotIdx>=0) ? dotIdx : file.length(), QString("_") + QString::number(num) );
 
224
      }
 
225
      curUrl.setFileName(file);
 
226
 
 
227
      // Increment the counter for both the old and the new filename
 
228
      if ( !renameNumbering.contains(origFile))
 
229
        renameNumbering[origFile] = 1;
 
230
      else
 
231
        renameNumbering[origFile]++;
 
232
 
 
233
      if ( file != origFile ) {
 
234
        if ( !renameNumbering.contains(file))
 
235
          renameNumbering[file] = 1;
 
236
        else
 
237
          renameNumbering[file]++;
 
238
      }
 
239
 
 
240
 
 
241
      if ( !overwriteAll && KIO::NetAccess::exists( curUrl, KIO::NetAccess::DestinationSide, parent ) ) {
 
242
        if ( contents.count() == 1 ) {
 
243
          if ( KMessageBox::warningContinueCancel( parent,
 
244
                i18n( "A file named <br><filename>%1</filename><br>already exists.<br><br>Do you want to overwrite it?",
 
245
                  curUrl.fileName() ),
 
246
                i18n( "File Already Exists" ), KGuiItem(i18n("&Overwrite")) ) == KMessageBox::Cancel) {
 
247
            continue;
 
248
          }
 
249
        }
 
250
        else {
 
251
          int button = KMessageBox::warningYesNoCancel(
 
252
                parent,
 
253
                i18n( "A file named <br><filename>%1</filename><br>already exists.<br><br>Do you want to overwrite it?",
 
254
                  curUrl.fileName() ),
 
255
                i18n( "File Already Exists" ), KGuiItem(i18n("&Overwrite")),
 
256
                KGuiItem(i18n("Overwrite &All")) );
 
257
          if ( button == KMessageBox::Cancel )
 
258
            continue;
 
259
          else if ( button == KMessageBox::No )
 
260
            overwriteAll = true;
 
261
        }
 
262
      }
 
263
      // save
 
264
      const bool result = saveContent( parent, content, curUrl );
 
265
      if ( !result )
 
266
        globalResult = result;
 
267
    }
 
268
  }
 
269
 
 
270
  return globalResult;
 
271
}
 
272
 
 
273
bool Util::saveContent( QWidget *parent, KMime::Content* content, const KUrl& url )
 
274
{
 
275
  // FIXME: This is all horribly broken. First of all, creating a NodeHelper and then immediatley
 
276
  //        reading out the encryption/signature state will not work at all.
 
277
  //        Then, topLevel() will not work for attachments that are inside encrypted parts.
 
278
  //        What should actually be done is either passing in an ObjectTreeParser that has already
 
279
  //        parsed the message, or creating an OTP here (which would have the downside that the
 
280
  //        password dialog for decrypting messages is shown twice)
 
281
#if 0 // totally broken
 
282
  KMime::Content *topContent  = content->topLevel();
 
283
  MessageViewer::NodeHelper *mNodeHelper = new MessageViewer::NodeHelper;
 
284
  bool bSaveEncrypted = false;
 
285
  bool bEncryptedParts = mNodeHelper->encryptionState( content ) != MessageViewer::KMMsgNotEncrypted;
 
286
  if( bEncryptedParts )
 
287
    if( KMessageBox::questionYesNo( parent,
 
288
                                    i18n( "The part %1 of the message is encrypted. Do you want to keep the encryption when saving?",
 
289
                                          url.fileName() ),
 
290
                                    i18n( "KMail Question" ), KGuiItem(i18n("Keep Encryption")), KGuiItem(i18n("Do Not Keep")) ) ==
 
291
        KMessageBox::Yes )
 
292
      bSaveEncrypted = true;
 
293
 
 
294
  bool bSaveWithSig = true;
 
295
  if(mNodeHelper->signatureState( content ) != MessageViewer::KMMsgNotSigned )
 
296
    if( KMessageBox::questionYesNo( parent,
 
297
                                    i18n( "The part %1 of the message is signed. Do you want to keep the signature when saving?",
 
298
                                          url.fileName() ),
 
299
                                    i18n( "KMail Question" ), KGuiItem(i18n("Keep Signature")), KGuiItem(i18n("Do Not Keep")) ) !=
 
300
        KMessageBox::Yes )
 
301
      bSaveWithSig = false;
 
302
 
 
303
  QByteArray data;
 
304
  if( bSaveEncrypted || !bEncryptedParts) {
 
305
    KMime::Content *dataNode = content;
 
306
    QByteArray rawReplyString;
 
307
    bool gotRawReplyString = false;
 
308
    if ( !bSaveWithSig ) {
 
309
      if ( topContent->contentType()->mimeType() == "multipart/signed" )  {
 
310
        // carefully look for the part that is *not* the signature part:
 
311
        if ( ObjectTreeParser::findType( topContent, "application/pgp-signature", true, false ) ) {
 
312
          dataNode = ObjectTreeParser::findTypeNot( topContent, "application", "pgp-signature", true, false );
 
313
        } else if ( ObjectTreeParser::findType( topContent, "application/pkcs7-mime" , true, false ) ) {
 
314
          dataNode = ObjectTreeParser::findTypeNot( topContent, "application", "pkcs7-mime", true, false );
 
315
        } else {
 
316
          dataNode = ObjectTreeParser::findTypeNot( topContent, "multipart", "", true, false );
 
317
        }
 
318
      } else {
 
319
        EmptySource emptySource;
 
320
        ObjectTreeParser otp( &emptySource, 0, 0,false, false, false );
 
321
 
 
322
        // process this node and all it's siblings and descendants
 
323
        mNodeHelper->setNodeUnprocessed( dataNode, true );
 
324
        otp.parseObjectTree( dataNode );
 
325
 
 
326
        rawReplyString = otp.rawReplyString();
 
327
        gotRawReplyString = true;
 
328
      }
 
329
    }
 
330
    QByteArray cstr = gotRawReplyString
 
331
      ? rawReplyString
 
332
      : dataNode->decodedContent();
 
333
    data = KMime::CRLFtoLF( cstr );
 
334
  }
 
335
#else
 
336
  const QByteArray data = content->decodedContent();
 
337
  kWarning() << "Port the encryption/signature handling when saving a KMime::Content.";
 
338
#endif
 
339
  QDataStream ds;
 
340
  QFile file;
 
341
  KTemporaryFile tf;
 
342
  if ( url.isLocalFile() )
 
343
  {
 
344
    // save directly
 
345
    file.setFileName( url.toLocalFile() );
 
346
    if ( !file.open( QIODevice::WriteOnly ) )
 
347
    {
 
348
      KMessageBox::error( parent,
 
349
                          i18nc( "1 = file name, 2 = error string",
 
350
                                  "<qt>Could not write to the file<br><filename>%1</filename><br><br>%2",
 
351
                                  file.fileName(),
 
352
                                  file.errorString() ),
 
353
                          i18n( "Error saving attachment" ) );
 
354
      return false;
 
355
    }
 
356
 
 
357
    const int permissions = MessageViewer::Util::getWritePermissions();
 
358
    if ( permissions >= 0 )
 
359
      fchmod( file.handle(), permissions );
 
360
 
 
361
    ds.setDevice( &file );
 
362
  } else
 
363
  {
 
364
    // tmp file for upload
 
365
    tf.open();
 
366
    ds.setDevice( &tf );
 
367
  }
 
368
 
 
369
  const int bytesWritten = ds.writeRawData( data.data(), data.size() );
 
370
  if ( bytesWritten != data.size() ) {
 
371
    QFile *f = static_cast<QFile *>( ds.device() );
 
372
    KMessageBox::error( parent,
 
373
                        i18nc( "1 = file name, 2 = error string",
 
374
                               "<qt>Could not write to the file<br><filename>%1</filename><br><br>%2",
 
375
                               f->fileName(),
 
376
                               f->errorString() ),
 
377
                        i18n( "Error saving attachment" ) );
 
378
    // Remove the newly created empty or partial file
 
379
    f->remove();
 
380
    return false;
 
381
  }
 
382
 
 
383
  if ( !url.isLocalFile() )
 
384
    {
 
385
      // QTemporaryFile::fileName() is only defined while the file is open
 
386
      QString tfName = tf.fileName();
 
387
      tf.close();
 
388
      if ( !KIO::NetAccess::upload( tfName, url, parent ) )
 
389
        {
 
390
          KMessageBox::error( parent,
 
391
                              i18nc( "1 = file name, 2 = error string",
 
392
                                     "<qt>Could not write to the file<br><filename>%1</filename><br><br>%2",
 
393
                                     url.prettyUrl(),
 
394
                                     KIO::NetAccess::lastErrorString() ),
 
395
                              i18n( "Error saving attachment" ) );
 
396
          return false;
 
397
        }
 
398
    }
 
399
  else
 
400
    file.close();
 
401
 
 
402
#if 0
 
403
  mNodeHelper->removeTempFiles();
 
404
  delete mNodeHelper;
 
405
#endif
 
406
  return true;
 
407
}
 
408
 
 
409
 
 
410
int Util::getWritePermissions()
 
411
{
 
412
  // #79685, #232001 by default use the umask the user defined, but let it be configurable
 
413
  if ( MessageCore::GlobalSettings::self()->disregardUmask() ) {
 
414
    return S_IRUSR | S_IWUSR;
 
415
  } else {
 
416
    return -1;
 
417
  }
 
418
}
 
419
 
124
420