80
QStringList Util::supportedEncodings(bool usAscii)
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)
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) )
94
encodings.append( KGlobal::charsets()->descriptionForEncoding(*it) );
95
mimeNames.insert( mimeName, true );
96
// kDebug() << "added" << mimeName;
101
encodings.prepend(KGlobal::charsets()->descriptionForEncoding("us-ascii") );
105
//-----------------------------------------------------------------------------
106
QString Util::fixEncoding( const QString &encoding )
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-" );
115
return returnEncoding;
118
//-----------------------------------------------------------------------------
119
QString Util::encodingForName( const QString &descriptiveName )
121
QString encoding = KGlobal::charsets()->encodingForName( descriptiveName );
122
return Util::fixEncoding( encoding );
120
QList<KMime::Content*> Util::allContents( const KMime::Content *message )
122
KMime::Content::List result;
123
KMime::Content *child = MessageCore::NodeHelper::firstChild( message );
126
result += allContents( child );
128
KMime::Content *next = MessageCore::NodeHelper::nextSibling( message );
131
result += allContents( next );
137
QList<KMime::Content*> Util::extractAttachments( const KMime::Message *message )
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;
151
contents.erase( delIt );
159
bool Util::saveContents( QWidget *parent, const QList<KMime::Content*> &contents )
162
if ( contents.count() > 1 ) {
164
dirUrl = KFileDialog::getExistingDirectoryUrl( KUrl( "kfiledialog:///saveAttachment" ),
166
i18n( "Save Attachments To" ) );
167
if ( !dirUrl.isValid() ) {
171
// we may not get a slash-terminated url out of KFileDialog
172
dirUrl.adjustPath( KUrl::AddTrailingSlash );
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" );
182
url = KFileDialog::getSaveUrl( KUrl( "kfiledialog:///saveAttachment/" + fileName ),
185
i18n( "Save Attachment" ) );
186
if ( url.isEmpty() ) {
191
QMap< QString, int > renameNumbering;
193
bool globalResult = true;
194
int unnamedAtmCount = 0;
195
bool overwriteAll = false;
196
foreach( KMime::Content *content, contents ) {
198
if ( !dirUrl.isEmpty() ) {
200
QString fileName = MessageViewer::NodeHelper::fileName( content );
201
fileName = MessageCore::StringUtil::cleanFileName( fileName );
202
if ( fileName.isEmpty() ) {
204
fileName = i18nc( "filename for the %1-th unnamed attachment",
205
"attachment.%1", unnamedAtmCount );
207
curUrl.setFileName( fileName );
212
if ( !curUrl.isEmpty() ) {
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;
219
while ( renameNumbering.contains(file) ) {
221
int num = renameNumbering[file] + 1;
222
int dotIdx = file.lastIndexOf('.');
223
file = file.insert( (dotIdx>=0) ? dotIdx : file.length(), QString("_") + QString::number(num) );
225
curUrl.setFileName(file);
227
// Increment the counter for both the old and the new filename
228
if ( !renameNumbering.contains(origFile))
229
renameNumbering[origFile] = 1;
231
renameNumbering[origFile]++;
233
if ( file != origFile ) {
234
if ( !renameNumbering.contains(file))
235
renameNumbering[file] = 1;
237
renameNumbering[file]++;
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?",
246
i18n( "File Already Exists" ), KGuiItem(i18n("&Overwrite")) ) == KMessageBox::Cancel) {
251
int button = KMessageBox::warningYesNoCancel(
253
i18n( "A file named <br><filename>%1</filename><br>already exists.<br><br>Do you want to overwrite it?",
255
i18n( "File Already Exists" ), KGuiItem(i18n("&Overwrite")),
256
KGuiItem(i18n("Overwrite &All")) );
257
if ( button == KMessageBox::Cancel )
259
else if ( button == KMessageBox::No )
264
const bool result = saveContent( parent, content, curUrl );
266
globalResult = result;
273
bool Util::saveContent( QWidget *parent, KMime::Content* content, const KUrl& url )
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?",
290
i18n( "KMail Question" ), KGuiItem(i18n("Keep Encryption")), KGuiItem(i18n("Do Not Keep")) ) ==
292
bSaveEncrypted = true;
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?",
299
i18n( "KMail Question" ), KGuiItem(i18n("Keep Signature")), KGuiItem(i18n("Do Not Keep")) ) !=
301
bSaveWithSig = false;
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 );
316
dataNode = ObjectTreeParser::findTypeNot( topContent, "multipart", "", true, false );
319
EmptySource emptySource;
320
ObjectTreeParser otp( &emptySource, 0, 0,false, false, false );
322
// process this node and all it's siblings and descendants
323
mNodeHelper->setNodeUnprocessed( dataNode, true );
324
otp.parseObjectTree( dataNode );
326
rawReplyString = otp.rawReplyString();
327
gotRawReplyString = true;
330
QByteArray cstr = gotRawReplyString
332
: dataNode->decodedContent();
333
data = KMime::CRLFtoLF( cstr );
336
const QByteArray data = content->decodedContent();
337
kWarning() << "Port the encryption/signature handling when saving a KMime::Content.";
342
if ( url.isLocalFile() )
345
file.setFileName( url.toLocalFile() );
346
if ( !file.open( QIODevice::WriteOnly ) )
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",
352
file.errorString() ),
353
i18n( "Error saving attachment" ) );
357
const int permissions = MessageViewer::Util::getWritePermissions();
358
if ( permissions >= 0 )
359
fchmod( file.handle(), permissions );
361
ds.setDevice( &file );
364
// tmp file for upload
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",
377
i18n( "Error saving attachment" ) );
378
// Remove the newly created empty or partial file
383
if ( !url.isLocalFile() )
385
// QTemporaryFile::fileName() is only defined while the file is open
386
QString tfName = tf.fileName();
388
if ( !KIO::NetAccess::upload( tfName, url, parent ) )
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",
394
KIO::NetAccess::lastErrorString() ),
395
i18n( "Error saving attachment" ) );
403
mNodeHelper->removeTempFiles();
410
int Util::getWritePermissions()
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;