qtmobility/src/messaging/win32wce/qmailnamespace.cpp
changeset 1 2b40d63a9c3d
child 11 06b8e2af4411
equal deleted inserted replaced
0:cfcbf08528c4 1:2b40d63a9c3d
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qmailnamespace.h"
       
    43 #include <QApplication>
       
    44 #include <QDir>
       
    45 #include <QDebug>
       
    46 #include <QDir>
       
    47 #include <QtDebug>
       
    48 #include <QMutex>
       
    49 #include <QRegExp>
       
    50 #include <stdio.h>
       
    51 #if !defined(Q_OS_WIN) || !defined(_WIN32_WCE)
       
    52 // Not available for windows mobile?
       
    53 #include <QSqlDatabase>
       
    54 #endif
       
    55 
       
    56 #ifdef Q_OS_WIN
       
    57 #include <windows.h>
       
    58 #else
       
    59 #include <stdlib.h>
       
    60 #include <unistd.h>
       
    61 #include <errno.h>
       
    62 #include <sys/types.h>
       
    63 #include <sys/stat.h>
       
    64 #include <fcntl.h>
       
    65 #endif
       
    66 
       
    67 static const char* QMF_DATA_ENV="QMF_DATA";
       
    68 static const char* QMF_PLUGINS_ENV="QMF_PLUGINS";
       
    69 static const char* QMF_SERVER_ENV="QMF_SERVER";
       
    70 static const char* QMF_SETTINGS_ENV="QMF_SETTINGS";
       
    71 
       
    72 /*!
       
    73     \namespace QMail
       
    74 
       
    75     \brief The QMail namespace contains miscellaneous functionality used by the Messaging framework.
       
    76 */
       
    77 
       
    78 /*!
       
    79     \fn StringType QMail::unquoteString(const StringType& src)
       
    80 
       
    81     If \a src has double-quote as the first and last characters, return the string between those characters;
       
    82     otherwise, return the original string.
       
    83 */
       
    84 
       
    85 /*!
       
    86     \fn StringType QMail::quoteString(const StringType& src)
       
    87 
       
    88     Returns \a src surrounded by double-quotes, which are added if not already present.
       
    89 */
       
    90 
       
    91 #ifdef Q_OS_WIN
       
    92 static QMap<int, HANDLE> lockedFiles;
       
    93 #endif
       
    94 
       
    95 #if !defined(Q_OS_WIN) || !defined(_WIN32_WCE) // Not supported on windows mobile
       
    96 /*!
       
    97     Convenience function that attempts to obtain a lock on a file with name \a lockFile.
       
    98     It is not necessary to create \a lockFile as this file is created temporarily.
       
    99 
       
   100     Returns the id of the lockFile if successful or \c -1 for failure.
       
   101 
       
   102     \sa QMail::fileUnlock()
       
   103 */
       
   104 int QMail::fileLock(const QString& lockFile)
       
   105 {
       
   106     QString path = QDir::tempPath() + "/" + lockFile;
       
   107 
       
   108 #ifdef Q_OS_WIN
       
   109     static int lockedCount = 0;
       
   110 
       
   111 	if (!QFile::exists(path)) {
       
   112 		QFile file(path);
       
   113 		file.open(QIODevice::WriteOnly);
       
   114 		file.close();
       
   115 	}
       
   116 
       
   117     HANDLE handle = ::CreateFile(reinterpret_cast<const wchar_t*>(path.utf16()),
       
   118                                  GENERIC_READ,
       
   119                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
       
   120                                  NULL,
       
   121                                  OPEN_EXISTING,
       
   122                                  FILE_ATTRIBUTE_NORMAL,
       
   123                                  NULL);
       
   124     if (handle == INVALID_HANDLE_VALUE) {
       
   125         qWarning() << "Unable to open file for locking:" << path;
       
   126     } else {
       
   127         if (::LockFile(handle, 0, 0, 1, 0) == FALSE) {
       
   128             qWarning() << "Unable to lock file:" << path;
       
   129         } else {
       
   130             ++lockedCount;
       
   131             lockedFiles.insert(lockedCount, handle);
       
   132             return lockedCount;
       
   133         }
       
   134     }
       
   135 
       
   136     return -1;
       
   137 #else
       
   138     struct flock fl;
       
   139 
       
   140     fl.l_type = F_WRLCK;
       
   141     fl.l_whence = SEEK_SET;
       
   142     fl.l_start = 0;
       
   143     fl.l_len = 0;
       
   144 
       
   145     int fdlock = -1;
       
   146     if((fdlock = ::open(path.toLatin1(), O_WRONLY|O_CREAT, 0666)) == -1)
       
   147         return -1;
       
   148 
       
   149     if(::fcntl(fdlock, F_SETLK, &fl) == -1)
       
   150         return -1;
       
   151 
       
   152     return fdlock;
       
   153 #endif
       
   154 }
       
   155 
       
   156 /*!
       
   157     Convenience function that attempts to unlock the file with identifier \a id that was locked by \c QMail::fileLock.
       
   158 
       
   159     Returns \c true for success or \c false otherwise.
       
   160 
       
   161     \sa QMail::fileLock()
       
   162 */
       
   163 bool QMail::fileUnlock(int id)
       
   164 {
       
   165 #ifdef Q_OS_WIN
       
   166     QMap<int, HANDLE>::iterator it = lockedFiles.find(id);
       
   167     if (it != lockedFiles.end()) {
       
   168         if (::UnlockFile(it.value(), 0, 0, 1, 0) == FALSE) {
       
   169             qWarning() << "Unable to unlock file:" << lastSystemErrorMessage();
       
   170         } else {
       
   171             if (::CloseHandle(it.value()) == FALSE) {
       
   172                 qWarning() << "Unable to close handle:" << lastSystemErrorMessage();
       
   173             }
       
   174 
       
   175             lockedFiles.erase(it);
       
   176             return true;
       
   177         }
       
   178     }
       
   179 
       
   180     return false;
       
   181 #else
       
   182     struct flock fl;
       
   183 
       
   184     fl.l_type = F_UNLCK;
       
   185     fl.l_whence = SEEK_SET;
       
   186     fl.l_start = 0;
       
   187     fl.l_len = 0;
       
   188 
       
   189     int result = -1;
       
   190 
       
   191     if((result = ::fcntl(id,F_SETLK, &fl)) == -1)
       
   192         return false;
       
   193 
       
   194     if((result = ::close(id)) == -1)
       
   195         return false;
       
   196 
       
   197     return true;
       
   198 #endif
       
   199 }
       
   200 #endif
       
   201 
       
   202 /*!
       
   203     Returns the path to where the Messaging framework will store its data files.
       
   204 */
       
   205 QString QMail::dataPath()
       
   206 {
       
   207     static QString dataEnv(qgetenv(QMF_DATA_ENV));
       
   208     if(!dataEnv.isEmpty())
       
   209         return dataEnv + "/";
       
   210     //default to ~/.qmf if not env set
       
   211     return QDir::homePath() + "/.qmf/";
       
   212 }
       
   213 
       
   214 /*!
       
   215     Returns the path to where the Messaging framework will store its temporary files.
       
   216 */
       
   217 QString QMail::tempPath()
       
   218 {
       
   219     return QDir::tempPath();
       
   220 }
       
   221 
       
   222 /*!
       
   223     Returns the path to where the Messaging framework will look for its plugin directories
       
   224 */
       
   225 QString QMail::pluginsPath()
       
   226 {
       
   227     static QString pluginsEnv(qgetenv(QMF_PLUGINS_ENV));
       
   228     if(!pluginsEnv.isEmpty())
       
   229         return pluginsEnv + "/";
       
   230     //default to "." if no env set
       
   231     return pluginsEnv;
       
   232 }
       
   233 
       
   234 /*!
       
   235     Returns the path to where the Messaging framework will search for SSL certificates.
       
   236 */
       
   237 QString QMail::sslCertsPath()
       
   238 {
       
   239     return "/etc/ssl/certs/";
       
   240 }
       
   241 
       
   242 /*!
       
   243     Returns the path to where the Messaging framework will invoke the messageserver process.
       
   244 */
       
   245 QString QMail::messageServerPath()
       
   246 {
       
   247     static QString serverEnv(qgetenv(QMF_SERVER_ENV));
       
   248     if(!serverEnv.isEmpty())
       
   249         return serverEnv + "/";
       
   250     return QApplication::applicationDirPath() + "/";
       
   251 }
       
   252 
       
   253 /*!
       
   254     Returns the path to where the Messaging framework will search for settings information.
       
   255 */
       
   256 QString QMail::messageSettingsPath()
       
   257 {
       
   258     static QString settingsEnv(qgetenv(QMF_SETTINGS_ENV));
       
   259     if(!settingsEnv.isEmpty())
       
   260         return settingsEnv + "/";
       
   261     return QApplication::applicationDirPath() + "/";
       
   262 }
       
   263 
       
   264 #if !defined(Q_OS_WIN) || !defined(_WIN32_WCE) // Not supported on windows mobile
       
   265 /*!
       
   266     Returns the database where the Messaging framework will store its message meta-data. 
       
   267     If the database does not exist, it is created.
       
   268 */
       
   269 QSqlDatabase QMail::createDatabase()
       
   270 {
       
   271     static bool init = false;
       
   272     QSqlDatabase db;
       
   273     if(!init)
       
   274     {
       
   275         db = QSqlDatabase::addDatabase("QSQLITE");
       
   276         QDir dp(dataPath());
       
   277         if(!dp.exists())
       
   278             if(!dp.mkpath(dataPath()))
       
   279                 qCritical() << "Cannot create data path";
       
   280         db.setDatabaseName(dataPath() + "qmailstore.db");
       
   281         if(!db.open())
       
   282             qCritical() << "Cannot open database";
       
   283         else
       
   284             init = true;
       
   285     }
       
   286     return db;
       
   287 }
       
   288 #endif
       
   289 
       
   290 /*!
       
   291     \internal
       
   292     Returns the next word, given the input and starting position.
       
   293 */
       
   294 static QString nextString( const char *line, int& posn )
       
   295 {
       
   296     if ( line[posn] == '\0' )
       
   297         return QString::null;
       
   298     int end = posn;
       
   299     char ch;
       
   300     for (;;) {
       
   301         ch = line[end];
       
   302         if ( ch == '\0' || ch == ' ' || ch == '\t' ||
       
   303              ch == '\r' || ch == '\n' ) {
       
   304             break;
       
   305         }
       
   306         ++end;
       
   307     }
       
   308     const char *result = line + posn;
       
   309     int resultLen = end - posn;
       
   310     for (;;) {
       
   311         ch = line[end];
       
   312         if ( ch == '\0' )
       
   313             break;
       
   314         if ( ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n' )
       
   315             break;
       
   316         ++end;
       
   317     }
       
   318     posn = end;
       
   319     return QString::fromLocal8Bit(result, resultLen);
       
   320 }
       
   321 
       
   322 typedef QHash<QString, QString> typeForType;
       
   323 Q_GLOBAL_STATIC(typeForType, typeFor);
       
   324 typedef QHash<QString, QStringList> extForType;
       
   325 Q_GLOBAL_STATIC(extForType, extFor);
       
   326 
       
   327 /*!
       
   328     \internal
       
   329     Loads the mime type to extensions mapping
       
   330 */
       
   331 static void loadExtensions()
       
   332 {
       
   333     QMutex mutex;
       
   334     mutex.lock();
       
   335     static bool loaded = false;
       
   336 
       
   337     if(loaded)
       
   338     {
       
   339         mutex.unlock();
       
   340         return;
       
   341     }
       
   342 
       
   343     QFile file(":qtopiamail/mime.types");
       
   344     if ( file.open(QIODevice::ReadOnly) ) {
       
   345         char line[1024];
       
   346 
       
   347         while (file.readLine(line, sizeof(line)) > 0) {
       
   348             if (line[0] == '\0' || line[0] == '#')
       
   349                 continue;
       
   350             int posn = 0;
       
   351             QString id = nextString(line, posn);
       
   352             if ( id.isEmpty() )
       
   353                 continue;
       
   354             id = id.toLower();
       
   355 
       
   356             QStringList exts = extFor()->value(id);
       
   357 
       
   358             for( QString ext = nextString( line, posn ); !ext.isEmpty(); ext = nextString(line, posn).toLower() )
       
   359             {
       
   360                 if( !exts.contains( ext ) )
       
   361                 {
       
   362                     exts.append( ext );
       
   363 
       
   364                     typeFor()->insert(ext, id);
       
   365                 }
       
   366             }
       
   367             (*extFor())[ id ] = exts;
       
   368         }
       
   369         loaded = true;
       
   370     }
       
   371     mutex.unlock();
       
   372 }
       
   373 
       
   374 /*!
       
   375     Returns the string mime type based on the filename \a filename.
       
   376 */
       
   377 QString QMail::mimeTypeFromFileName(const QString& filename)
       
   378 {
       
   379     if (filename.isEmpty())
       
   380         return QString();
       
   381 
       
   382     loadExtensions();
       
   383 
       
   384     // do a case insensitive search for a known mime type.
       
   385     QString lwrExtOrId = filename.toLower();
       
   386     QHash<QString,QStringList>::const_iterator it = extFor()->find(lwrExtOrId);
       
   387     if (it != extFor()->end()) {
       
   388         return lwrExtOrId;
       
   389     }
       
   390 
       
   391     // either it doesnt have exactly one mime-separator, or it has
       
   392     // a path separator at the beginning
       
   393     QString mime_sep = QLatin1String("/");
       
   394     bool doesntLookLikeMimeString = (filename.count(mime_sep) != 1) || (filename[0] == QDir::separator());
       
   395 
       
   396     if (doesntLookLikeMimeString || QFile::exists(filename)) {
       
   397         int dot = filename.lastIndexOf('.');
       
   398         QString ext = dot >= 0 ? filename.mid(dot+1) : filename;
       
   399 
       
   400         QHash<QString,QString>::const_iterator it = typeFor()->find(ext.toLower());
       
   401         if (it != typeFor()->end()) {
       
   402             return *it;
       
   403         }
       
   404 
       
   405         const char elfMagic[] = { '\177', 'E', 'L', 'F', '\0' };
       
   406         QFile ef(filename);
       
   407         if (ef.exists() && (ef.size() > 5) && ef.open(QIODevice::ReadOnly) && (ef.peek(5) == elfMagic)) { // try to find from magic
       
   408             return QLatin1String("application/x-executable");  // could be a shared library or an exe
       
   409         } else {
       
   410             return QLatin1String("application/octet-stream");
       
   411         }
       
   412     }
       
   413 
       
   414     // could be something like application/vnd.oma.rights+object
       
   415     return lwrExtOrId;
       
   416 }
       
   417 
       
   418 /*!
       
   419     Returns a list of valid file extensions for the mime type string \a mimeType
       
   420     or an empty list if the mime type is unrecognized.
       
   421 */
       
   422 QStringList QMail::extensionsForMimeType(const QString& mimeType)
       
   423 {
       
   424     loadExtensions();
       
   425     return extFor()->value(mimeType);
       
   426 }
       
   427 
       
   428 /*!
       
   429     Suspends the current process for \a usecs microseconds.
       
   430 */
       
   431 void QMail::usleep(unsigned long usecs)
       
   432 {
       
   433 #ifdef Q_OS_WIN
       
   434     ::Sleep((usecs + 500) / 1000);
       
   435 #else
       
   436     static const int factor(1000 * 1000);
       
   437 
       
   438     unsigned long seconds(usecs / factor);
       
   439     usecs = (usecs % factor);
       
   440 
       
   441     if (seconds) {
       
   442         ::sleep(seconds);
       
   443     }
       
   444     if (!seconds || usecs) {
       
   445         ::usleep(usecs);
       
   446     }
       
   447 #endif
       
   448 }
       
   449 
       
   450 /*!
       
   451     Returns the 'base' form of \a subject, using the transformation defined by RFC5256.
       
   452     If the original subject contains any variant of the tokens "Re" or "Fwd" recognized by
       
   453     RFC5256, then \a replyOrForward will be set to true.
       
   454 */
       
   455 QString QMail::baseSubject(const QString& subject, bool *replyOrForward)
       
   456 {
       
   457     // Implements the conversion from subject to 'base subject' defined by RFC 5256
       
   458     int pos = 0;
       
   459     QString result(subject);
       
   460 
       
   461     bool repeat = false;
       
   462     do {
       
   463         repeat = false;
       
   464 
       
   465         // Remove any subj-trailer
       
   466         QRegExp subjTrailer("(?:"
       
   467                                 "[ \\t]+"               // WSP
       
   468                             "|"
       
   469                                 "(\\([Ff][Ww][Dd]\\))"    // "(fwd)"
       
   470                             ")$");
       
   471         while ((pos = subjTrailer.indexIn(result)) != -1) {
       
   472             if (!subjTrailer.cap(1).isEmpty()) {
       
   473                 *replyOrForward = true;
       
   474             }
       
   475             result = result.left(pos);
       
   476         }
       
   477 
       
   478         bool modified = false;
       
   479         do {
       
   480             modified = false;
       
   481 
       
   482             // Remove any subj-leader
       
   483             QRegExp subjLeader("^(?:"
       
   484                                     "[ \\t]+"       // WSP
       
   485                                "|"
       
   486                                     "(?:\\[[^\\[\\]]*\\][ \\t]*)*"        // ( '[' 'blobchar'* ']' WSP* )*
       
   487                                     "([Rr][Ee]|[Ff][Ww][Dd]?)[ \\t]*"   // ( "Re" | "Fw" | "Fwd") WSP*
       
   488                                     "(?:\\[[^\\[\\]]*\\][ \\t]*)?"        // optional: ( '[' 'blobchar'* ']' WSP* )
       
   489                                     ":"                                 // ':'
       
   490                                ")");
       
   491             while ((pos = subjLeader.indexIn(result)) == 0) {
       
   492                 if (!subjLeader.cap(1).isEmpty()) {
       
   493                     *replyOrForward = true;
       
   494                 }
       
   495                 result = result.mid(subjLeader.cap(0).length());
       
   496                 modified = true;
       
   497             }
       
   498 
       
   499             // Remove subj-blob, if there would be a remainder
       
   500             QRegExp subjBlob("^(\\[[^\\[\\]]*\\][ \\t]*)");             // '[' 'blobchar'* ']' WSP*
       
   501             if ((subjBlob.indexIn(result) == 0) && (subjBlob.cap(0).length() < result.length())) {
       
   502                 result = result.mid(subjBlob.cap(0).length());
       
   503                 modified = true;
       
   504             }
       
   505         } while (modified);
       
   506 
       
   507         // Remove subj-fwd-hdr and subj-fwd-trl if both are present
       
   508         QRegExp subjFwdHdr("^\\[[Ff][Ww][Dd]:");
       
   509         QRegExp subjFwdTrl("\\]$");
       
   510         if ((subjFwdHdr.indexIn(result) == 0) && (subjFwdTrl.indexIn(result) != -1)) {
       
   511             *replyOrForward = true;
       
   512             result = result.mid(subjFwdHdr.cap(0).length(), result.length() - (subjFwdHdr.cap(0).length() + subjFwdTrl.cap(0).length()));
       
   513             repeat = true;
       
   514         }
       
   515     } while (repeat);
       
   516 
       
   517     return result;
       
   518 }
       
   519 
       
   520 static QString normaliseIdentifier(const QString& str)
       
   521 {
       
   522     // Don't permit space, tab or quote marks
       
   523     static const QChar skip[] = { QChar(' '), QChar('\t'), QChar('"') };
       
   524 
       
   525     QString result;
       
   526     result.reserve(str.length());
       
   527 
       
   528     QString::const_iterator it = str.begin(), end = str.end();
       
   529     while (it != end) {
       
   530         if ((*it != skip[0]) && (*it != skip[1]) && (*it != skip[2])) {
       
   531             result.append(*it);
       
   532         }
       
   533         ++it;
       
   534     }
       
   535 
       
   536     return result;
       
   537 }
       
   538 
       
   539 /*!
       
   540     Returns the sequence of message identifiers that can be extracted from \a str.
       
   541     Message identifiers must conform to the definition given by RFC 5256.
       
   542 */
       
   543 QStringList QMail::messageIdentifiers(const QString& str)
       
   544 {
       
   545     QStringList result;
       
   546 
       
   547     QRegExp identifierPattern("("
       
   548                                 "(?:[ \\t]*)"           // Optional leading whitespace
       
   549                                 "[^ \\t\\<\\>@]+"       // Leading part
       
   550                                 "(?:[ \\t]*)"           // Optional whitespace allowed before '@'?
       
   551                                 "@"
       
   552                                 "(?:[ \\t]*)"           // Optional whitespace allowed after '@'?
       
   553                                 "[^ \\t\\<\\>]+"        // Trailing part
       
   554                               ")");
       
   555 
       
   556     // Extracts message identifiers from \a str, matching the definition used in RFC 5256
       
   557     int index = str.indexOf('<');
       
   558     if (index != -1) {
       
   559         // This may contain other information besides the IDs delimited by < and >
       
   560         do {
       
   561             // Extract only the delimited content
       
   562             if (str.indexOf(identifierPattern, index + 1) == (index + 1)) {
       
   563                 result.append(normaliseIdentifier(identifierPattern.cap(1)));
       
   564                 index += identifierPattern.cap(0).length();
       
   565             } else {
       
   566                 index += 1;
       
   567             }
       
   568 
       
   569             index = str.indexOf('<', index);
       
   570         } while (index != -1);
       
   571     } else {
       
   572         // No delimiters - consider the entirety as an identifier
       
   573         if (str.indexOf(identifierPattern) != -1) {
       
   574             result.append(normaliseIdentifier(identifierPattern.cap(1)));
       
   575         }
       
   576     }
       
   577 
       
   578     return result;
       
   579 }
       
   580 
       
   581 /*!
       
   582     Returns the text describing the last error reported by the underlying platform.
       
   583 */
       
   584 QString QMail::lastSystemErrorMessage()
       
   585 {
       
   586 #ifdef Q_OS_WIN
       
   587     LPVOID buffer;
       
   588 
       
   589     ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
       
   590                     NULL,
       
   591                     ::GetLastError(),
       
   592                     0,
       
   593                     reinterpret_cast<LPTSTR>(&buffer),
       
   594                     0,
       
   595                     NULL);
       
   596 
       
   597     QString result(QString::fromUtf16(reinterpret_cast<const ushort*>(buffer)));
       
   598     ::LocalFree(buffer);
       
   599 
       
   600     return result;
       
   601 #else
       
   602     return QString(::strerror(errno));
       
   603 #endif
       
   604 }
       
   605