aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/basetypes
diff options
context:
space:
mode:
authorGravatar DINH Viet Hoa <hoa@sprw.me>2013-01-11 01:08:18 -0800
committerGravatar DINH Viet Hoa <hoa@sprw.me>2013-01-11 01:08:18 -0800
commit739b68a69682d80d8247d4465eae7b182acc9da0 (patch)
tree34c33738bc0761d30c13f7f9b88fdf0d5c8cb4a9 /src/core/basetypes
parentf83e0e9ba3da2b8887f849483506d5de8f1d2c54 (diff)
first commit
Diffstat (limited to 'src/core/basetypes')
-rw-r--r--src/core/basetypes/.DS_Storebin0 -> 6148 bytes
-rw-r--r--src/core/basetypes/MCArray.cc229
-rw-r--r--src/core/basetypes/MCArray.h49
-rw-r--r--src/core/basetypes/MCAssert.cc12
-rw-r--r--src/core/basetypes/MCAssert.h13
-rw-r--r--src/core/basetypes/MCAutoreleasePool.cc119
-rw-r--r--src/core/basetypes/MCAutoreleasePool.h34
-rw-r--r--src/core/basetypes/MCBaseTypes.h21
-rw-r--r--src/core/basetypes/MCData.cc526
-rw-r--r--src/core/basetypes/MCData.h58
-rw-r--r--src/core/basetypes/MCHash.cc12
-rw-r--r--src/core/basetypes/MCHash.h11
-rw-r--r--src/core/basetypes/MCHashMap.cc283
-rw-r--r--src/core/basetypes/MCHashMap.h45
-rw-r--r--src/core/basetypes/MCLog.cc30
-rw-r--r--src/core/basetypes/MCLog.h19
-rw-r--r--src/core/basetypes/MCMainThread.h11
-rw-r--r--src/core/basetypes/MCMainThread.mm60
-rw-r--r--src/core/basetypes/MCObject.cc180
-rw-r--r--src/core/basetypes/MCObject.h41
-rw-r--r--src/core/basetypes/MCOperation.cc45
-rw-r--r--src/core/basetypes/MCOperation.h33
-rw-r--r--src/core/basetypes/MCOperationCallback.h16
-rw-r--r--src/core/basetypes/MCOperationQueue.cc179
-rw-r--r--src/core/basetypes/MCOperationQueue.h45
-rw-r--r--src/core/basetypes/MCRange.cc12
-rw-r--r--src/core/basetypes/MCRange.h15
-rw-r--r--src/core/basetypes/MCSet.cc106
-rw-r--r--src/core/basetypes/MCSet.h42
-rw-r--r--src/core/basetypes/MCString.cc1958
-rw-r--r--src/core/basetypes/MCString.h123
-rw-r--r--src/core/basetypes/MCUtils.h34
-rw-r--r--src/core/basetypes/MCValue.cc314
-rw-r--r--src/core/basetypes/MCValue.h81
34 files changed, 4756 insertions, 0 deletions
diff --git a/src/core/basetypes/.DS_Store b/src/core/basetypes/.DS_Store
new file mode 100644
index 00000000..5008ddfc
--- /dev/null
+++ b/src/core/basetypes/.DS_Store
Binary files differ
diff --git a/src/core/basetypes/MCArray.cc b/src/core/basetypes/MCArray.cc
new file mode 100644
index 00000000..6b4df259
--- /dev/null
+++ b/src/core/basetypes/MCArray.cc
@@ -0,0 +1,229 @@
+#include "MCArray.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "MCAssert.h"
+#include "MCString.h"
+#include "MCLog.h"
+#include "MCUtils.h"
+
+using namespace mailcore;
+
+Array::Array()
+{
+ init();
+}
+
+Array::Array(Array * other) : Object()
+{
+ init();
+ for(unsigned int i = 0 ; i < other->count() ; i ++) {
+ Object * obj = other->objectAtIndex(i);
+ addObject(obj);
+ }
+}
+
+void Array::init()
+{
+ mArray = carray_new(4);
+}
+
+Array::~Array()
+{
+ removeAllObjects();
+ carray_free(mArray);
+}
+
+Array * Array::array()
+{
+ Array * result = new Array();
+ return (Array *) result->autorelease();
+}
+
+Array * Array::arrayWithObject(Object * obj)
+{
+ Array * result = new Array();
+ result->addObject(obj);
+ return (Array *) result->autorelease();
+}
+
+#if 0
+String * Array::className()
+{
+ return MCSTR("Array");
+}
+#endif
+
+String * Array::description()
+{
+ String * result = String::string();
+
+ result->appendUTF8Characters("[");
+ for(unsigned int i = 0 ; i < count() ; i ++) {
+ if (i != 0) {
+ result->appendUTF8Characters(",");
+ }
+ Object * obj = objectAtIndex(i);
+ result->appendString(obj->description());
+ }
+ result->appendUTF8Characters("]");
+
+ return result;
+}
+
+Object * Array::copy()
+{
+ return new Array(this);
+}
+
+unsigned int Array::count()
+{
+ return carray_count(mArray);
+}
+
+void Array::addObject(Object * obj)
+{
+ unsigned int idx;
+ obj->retain();
+ carray_add(mArray, obj, &idx);
+}
+
+void Array::removeObjectAtIndex(unsigned int idx)
+{
+ Object * obj = (Object *) carray_get(mArray, idx);
+ obj->release();
+ carray_delete_slow(mArray, idx);
+}
+
+void Array::removeObject(Object * obj)
+{
+ unsigned int idx = indexOfObject(obj);
+ removeObjectAtIndex(idx);
+}
+
+int Array::indexOfObject(Object * obj)
+{
+ for(unsigned int i = 0 ; i < count() ; i ++) {
+ Object * currentObj = objectAtIndex(i);
+ if (currentObj->isEqual(obj)) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+Object * Array::objectAtIndex(unsigned int idx)
+{
+ Object * obj = (Object *) carray_get(mArray, idx);
+ return obj;
+}
+
+void Array::replaceObject(unsigned int idx, Object * obj)
+{
+ if (idx < count()) {
+ Object * previousObject = (Object *) carray_get(mArray, idx);
+ previousObject->release();
+ obj->retain();
+ carray_set(mArray, idx, obj);
+ }
+ else if (idx == count()) {
+ addObject(obj);
+ }
+ else {
+ MCAssert(0);
+ }
+}
+
+void Array::insertObject(unsigned int idx, Object * obj)
+{
+ if (idx < count()) {
+ int count = carray_count(mArray) - idx;
+ carray_set_size(mArray, count + 1);
+ void ** p = carray_data(mArray);
+ memmove(p + idx + 1, p + idx, count * sizeof(* p));
+ obj->retain();
+ carray_set(mArray, idx, obj);
+ }
+ else if (idx == count()) {
+ addObject(obj);
+ }
+ else {
+ MCAssert(0);
+ }
+}
+
+void Array::removeAllObjects()
+{
+ for(unsigned int i = 0 ; i < count() ; i ++) {
+ Object * obj = objectAtIndex(i);
+ obj->release();
+ }
+ carray_set_size(mArray, 0);
+}
+
+void Array::addObjectsFromArray(Array * array)
+{
+ if (array == NULL)
+ return;
+
+ for(unsigned int i = 0 ; i < array->count() ; i ++) {
+ Object * obj = array->objectAtIndex(i);
+ addObject(obj);
+ }
+}
+
+Object * Array::lastObject()
+{
+ if (count() == 0)
+ return NULL;
+
+ return objectAtIndex(count() - 1);
+}
+
+bool Array::containsObject(Object * obj)
+{
+ return (indexOfObject(obj) != -1);
+}
+
+struct sortData {
+ int (* compare)(void *, void *, void *);
+ void * context;
+};
+
+static int sortCompare(struct sortData * data, Object ** pa, Object ** pb)
+{
+ Object * a;
+ Object * b;
+
+ a = * pa;
+ b = * pb;
+
+ return data->compare(a, b, data->context);
+}
+
+Array * Array::sortedArray(int (* compare)(void *, void *, void *), void * context)
+{
+ struct sortData data;
+ Array * result = (Array *) this->copy()->autorelease();
+ data.compare = compare;
+ data.context = context;
+ qsort_r(carray_data(result->mArray), carray_count(result->mArray),
+ sizeof(* carray_data(result->mArray)), this,
+ (int (*)(void *, const void *, const void *)) sortCompare);
+ return result;
+}
+
+String * Array::componentsJoinedByString(String * delimiter)
+{
+ String * result = String::string();
+ for(unsigned int i = 0 ; i < count() ; i ++) {
+ Object * obj = objectAtIndex(i);
+ if (result != 0) {
+ result->appendString(delimiter);
+ }
+ result->appendString(obj->description());
+ }
+ return result;
+}
diff --git a/src/core/basetypes/MCArray.h b/src/core/basetypes/MCArray.h
new file mode 100644
index 00000000..a0abc243
--- /dev/null
+++ b/src/core/basetypes/MCArray.h
@@ -0,0 +1,49 @@
+#ifndef __MAILCORE_MCARRAY_H_
+
+#define __MAILCORE_MCARRAY_H_
+
+#include <mailcore/MCObject.h>
+
+#include <libetpan/libetpan.h>
+
+namespace mailcore {
+
+ class String;
+
+ class Array : public Object {
+ private:
+ carray * mArray;
+ void init();
+ public:
+ Array();
+ Array(Array * o);
+ virtual ~Array();
+
+ static Array * array();
+ static Array * arrayWithObject(Object * obj);
+
+ //virtual String * className();
+ virtual String * description();
+ virtual Object * copy();
+
+ virtual unsigned int count();
+ virtual void addObject(Object * obj);
+ virtual void removeObjectAtIndex(unsigned int idx);
+ virtual void removeObject(Object * obj);
+ virtual int indexOfObject(Object * obj);
+ virtual Object * objectAtIndex(unsigned int idx);
+ virtual void replaceObject(unsigned int idx, Object * obj);
+ virtual void insertObject(unsigned int idx, Object * obj);
+ virtual void removeAllObjects();
+
+ virtual void addObjectsFromArray(Array * array);
+ virtual Object * lastObject();
+ virtual bool containsObject(Object * obj);
+
+ virtual Array * sortedArray(int (* compare)(void *, void *, void *), void * context);
+ virtual String * componentsJoinedByString(String * delimiter);
+ };
+
+}
+
+#endif
diff --git a/src/core/basetypes/MCAssert.cc b/src/core/basetypes/MCAssert.cc
new file mode 100644
index 00000000..afe9bfd5
--- /dev/null
+++ b/src/core/basetypes/MCAssert.cc
@@ -0,0 +1,12 @@
+#include "MCAssert.h"
+
+#include <stdio.h>
+
+void mailcore::assertInteral(const char * filename, unsigned int line, int cond, const char * condString)
+{
+ if (cond) {
+ return;
+ }
+
+ fprintf(stderr, "%s:%i: assert %s\n", filename, line, condString);
+}
diff --git a/src/core/basetypes/MCAssert.h b/src/core/basetypes/MCAssert.h
new file mode 100644
index 00000000..cc13aba8
--- /dev/null
+++ b/src/core/basetypes/MCAssert.h
@@ -0,0 +1,13 @@
+#ifndef __MAILCORE_MCASSERT_H_
+
+#define __MAILCORE_MCASSERT_H_
+
+#define MCAssert(cond) mailcore::assertInteral(__FILE__, __LINE__, cond, #cond)
+
+namespace mailcore {
+
+ void assertInteral(const char * filename, unsigned int line, int cond, const char * condString);
+
+}
+
+#endif
diff --git a/src/core/basetypes/MCAutoreleasePool.cc b/src/core/basetypes/MCAutoreleasePool.cc
new file mode 100644
index 00000000..7e43734f
--- /dev/null
+++ b/src/core/basetypes/MCAutoreleasePool.cc
@@ -0,0 +1,119 @@
+#include "MCAutoreleasePool.h"
+
+#include "MCString.h"
+#include "MCLog.h"
+#include "MCUtils.h"
+
+using namespace mailcore;
+
+pthread_key_t AutoreleasePool::autoreleasePoolStackKey;
+
+void AutoreleasePool::init()
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, initAutoreleasePoolStackKey);
+}
+
+AutoreleasePool::AutoreleasePool()
+{
+ mPoolObjects = carray_new(4);
+
+ unsigned int idx;
+ carray * stack = createAutoreleasePoolStackIfNeeded();
+ carray_add(stack, this, &idx);
+}
+
+AutoreleasePool::~AutoreleasePool()
+{
+ carray * stack = createAutoreleasePoolStackIfNeeded();
+ carray_delete_slow(stack, carray_count(stack) - 1);
+
+ unsigned int count = carray_count(mPoolObjects);
+ for(unsigned int i = 0 ; i < count ; i ++) {
+ Object * obj = (Object *) carray_get(mPoolObjects, i);
+ obj->release();
+ }
+ carray_free(mPoolObjects);
+}
+
+carray * AutoreleasePool::createAutoreleasePoolStackIfNeeded()
+{
+ init();
+ carray * stack = (carray *) pthread_getspecific(autoreleasePoolStackKey);
+ if (stack != NULL) {
+ return stack;
+ }
+
+ stack = carray_new(4);
+ pthread_setspecific(autoreleasePoolStackKey, stack);
+
+ return stack;
+}
+
+void AutoreleasePool::destroyAutoreleasePoolStack(void *)
+{
+ init();
+ carray * stack = (carray *) pthread_getspecific(autoreleasePoolStackKey);
+ if (carray_count(stack) != 0) {
+ MCLog("some autoreleasepool have not been released\n");
+ }
+ carray_free(stack);
+}
+
+void AutoreleasePool::initAutoreleasePoolStackKey()
+{
+ pthread_key_create(&autoreleasePoolStackKey, destroyAutoreleasePoolStack);
+}
+
+AutoreleasePool * AutoreleasePool::currentAutoreleasePool()
+{
+ init();
+ carray * stack;
+ stack = createAutoreleasePoolStackIfNeeded();
+ if (carray_count(stack) == 0) {
+ //fprintf(stderr, "no current autoreleasepool\n");
+ return NULL;
+ }
+
+ AutoreleasePool * pool;
+ pool = (AutoreleasePool *) carray_get(stack, carray_count(stack) - 1);
+ return pool;
+}
+
+void AutoreleasePool::add(Object * obj)
+{
+ unsigned int idx;
+ carray_add(mPoolObjects, obj, &idx);
+}
+
+void AutoreleasePool::autorelease(Object * obj)
+{
+ AutoreleasePool * pool = AutoreleasePool::currentAutoreleasePool();
+ if (pool == NULL) {
+ MCLog("-autorelease called with no current autoreleasepool\n");
+ return;
+ }
+ pool->add(obj);
+}
+
+String * AutoreleasePool::className()
+{
+ return MCSTR("AutoreleasePool");
+}
+
+String * AutoreleasePool::description()
+{
+ String * result = String::string();
+ result->appendUTF8Format("<%p:%p ", className(), this);
+ unsigned int count = carray_count(mPoolObjects);
+ for(unsigned int i = 0 ; i < count ; i ++) {
+ Object * obj = (Object *) carray_get(mPoolObjects, i);
+ if (i != 0) {
+ result->appendUTF8Characters(" ");
+ }
+ result->appendString(obj->description());
+ }
+ result->appendUTF8Characters(">");
+
+ return result;
+}
diff --git a/src/core/basetypes/MCAutoreleasePool.h b/src/core/basetypes/MCAutoreleasePool.h
new file mode 100644
index 00000000..ee30de87
--- /dev/null
+++ b/src/core/basetypes/MCAutoreleasePool.h
@@ -0,0 +1,34 @@
+#ifndef __MAILCORE_MCAUTORELEASEPOOL_H_
+
+#define __MAILCORE_MCAUTORELEASEPOOL_H_
+
+#include <mailcore/MCObject.h>
+#include <libetpan/libetpan.h>
+#include <pthread.h>
+
+namespace mailcore {
+
+ class AutoreleasePool : public Object {
+ private:
+ static void init();
+ static pthread_key_t autoreleasePoolStackKey;
+ carray * mPoolObjects;
+ static carray * createAutoreleasePoolStackIfNeeded();
+ static void destroyAutoreleasePoolStack(void *);
+ static void initAutoreleasePoolStackKey();
+ static AutoreleasePool * currentAutoreleasePool();
+ virtual void add(Object * obj);
+
+ public:
+ AutoreleasePool();
+ virtual ~AutoreleasePool();
+
+ virtual String * className();
+ virtual String * description();
+
+ static void autorelease(Object * obj);
+ };
+
+}
+
+#endif
diff --git a/src/core/basetypes/MCBaseTypes.h b/src/core/basetypes/MCBaseTypes.h
new file mode 100644
index 00000000..0f86dfb7
--- /dev/null
+++ b/src/core/basetypes/MCBaseTypes.h
@@ -0,0 +1,21 @@
+#ifndef __MAILCORE_MCBASETYPES_H_
+
+#define __MAILCORE_MCBASETYPES_H_
+
+#include <mailcore/MCAutoreleasePool.h>
+#include <mailcore/MCObject.h>
+#include <mailcore/MCValue.h>
+#include <mailcore/MCString.h>
+#include <mailcore/MCData.h>
+#include <mailcore/MCArray.h>
+#include <mailcore/MCHashMap.h>
+#include <mailcore/MCHash.h>
+#include <mailcore/MCLog.h>
+#include <mailcore/MCAssert.h>
+#include <mailcore/MCUtils.h>
+#include <mailcore/MCRange.h>
+#include <mailcore/MCOperation.h>
+#include <mailcore/MCOperationQueue.h>
+#include <mailcore/MCOperationCallback.h>
+
+#endif
diff --git a/src/core/basetypes/MCData.cc b/src/core/basetypes/MCData.cc
new file mode 100644
index 00000000..4550bff8
--- /dev/null
+++ b/src/core/basetypes/MCData.cc
@@ -0,0 +1,526 @@
+#include "MCData.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unicode/ucsdet.h>
+#include <libetpan/libetpan.h>
+
+#include "MCString.h"
+#include "MCHash.h"
+#include "MCUtils.h"
+
+#define DEFAULT_CHARSET "iso-8859-1"
+
+using namespace mailcore;
+
+void Data::allocate(unsigned int length)
+{
+ length ++;
+ if (length < mAllocated)
+ return;
+
+ if (mAllocated == 0) {
+ mAllocated = 4;
+ }
+ while (length > mAllocated) {
+ mAllocated *= 2;
+ }
+
+ mBytes = (char *) realloc(mBytes, mAllocated);
+}
+
+void Data::reset()
+{
+ free(mBytes);
+ mAllocated = 0;
+ mLength = 0;
+ mBytes = NULL;
+}
+
+Data::Data()
+{
+ mBytes = NULL;
+ reset();
+}
+
+Data::Data(Data * otherData) : Object()
+{
+ mBytes = NULL;
+ reset();
+ appendData(otherData);
+}
+
+Data::Data(const char * bytes, unsigned int length)
+{
+ mBytes = NULL;
+ reset();
+ appendBytes(bytes, length);
+}
+
+Data::Data(int capacity)
+{
+ mBytes = NULL;
+ reset();
+ allocate(capacity);
+}
+
+Data::~Data()
+{
+ reset();
+}
+
+Data * Data::dataWithBytes(const char * bytes, unsigned int length)
+{
+ Data * result = new Data(bytes, length);
+ return (Data *) result->autorelease();
+}
+
+char * Data::bytes()
+{
+ return mBytes;
+}
+
+unsigned int Data::length()
+{
+ return mLength;
+}
+
+void Data::appendData(Data * otherData)
+{
+ appendBytes(otherData->bytes(), otherData->length());
+}
+
+void Data::appendBytes(const char * bytes, unsigned int length)
+{
+ allocate(mLength + length);
+ memcpy(&mBytes[mLength], bytes, length);
+ mLength += length;
+}
+
+void Data::setBytes(const char * bytes, unsigned int length)
+{
+ reset();
+ appendBytes(bytes, length);
+}
+
+void Data::setData(Data * otherData)
+{
+ reset();
+ appendData(otherData);
+}
+
+#if 0
+String * Data::className()
+{
+ return MCSTR("Data");
+}
+#endif
+
+String * Data::description()
+{
+ return String::stringWithUTF8Format("<%s:%p %i bytes>", MCUTF8(className()), this, length());
+}
+
+Object * Data::copy()
+{
+ return new Data(this);
+}
+
+bool Data::isEqual(Object * otherObject)
+{
+ Data * otherData = (Data *) otherObject;
+ if (length() != otherData->length())
+ return false;
+ if (memcmp(bytes(), otherData->bytes(), mLength) != 0)
+ return false;
+ return true;
+}
+
+unsigned int Data::hash()
+{
+ return hashCompute(mBytes, mLength);
+}
+
+String * Data::stringWithDetectedCharset()
+{
+ String * charset;
+ String * result;
+
+ charset = charsetWithFilteredHTML(false);
+ if (charset == NULL) {
+ charset = MCSTR(DEFAULT_CHARSET);
+ }
+ result = emailStringWithCharset(charset);
+ if (result == NULL) {
+ result = stringWithCharset("utf-8");
+ }
+ return result;
+}
+
+String * Data::normalizeCharset(String * charset)
+{
+ if ((charset->caseInsensitiveCompare(MCSTR("iso-2022-jp")) == 0) ||
+ (charset->caseInsensitiveCompare(MCSTR("iso-2022-jp-2")) == 0)) {
+ charset = MCSTR("iso-2022-jp-2");
+ }
+ else if (charset->caseInsensitiveCompare(MCSTR("ks_c_5601-1987")) == 0) {
+ charset = MCSTR("euckr");
+ }
+ else if ((charset->caseInsensitiveCompare(MCSTR("iso-8859-8-i")) == 0) ||
+ (charset->caseInsensitiveCompare(MCSTR("iso-8859-8-e")) == 0)) {
+ charset = MCSTR("iso-8859-8");
+ }
+ else if ((charset->caseInsensitiveCompare(MCSTR("GB2312")) == 0) ||
+ (charset->caseInsensitiveCompare(MCSTR("GB_2312-80")) == 0)) {
+ charset = MCSTR("GBK");
+ }
+
+ return charset->lowercaseString();
+}
+
+String * Data::stringWithCharset(const char * charset)
+{
+ String * result = new String(this, charset);
+ return (String *) result->autorelease();
+}
+
+String * Data::emailStringWithCharset(String * charset)
+{
+ String * result;
+
+ charset = normalizeCharset(charset);
+
+ if (charset->isEqual(MCSTR("iso-2022-jp-2"))) {
+ const char * theBytes;
+ Data * data;
+
+ theBytes = bytes();
+ data = this;
+ if (length() > 0) {
+ unsigned int idx;
+
+ idx = length();
+ while ((theBytes[idx - 1] == '\n') || (theBytes[idx - 1] == '\r')) {
+ idx --;
+ if (idx == 0)
+ break;
+ }
+
+ if (idx != length()) {
+ data = Data::dataWithBytes(theBytes, idx);
+ }
+ }
+ result = data->stringWithCharset("iso-2022-jp-2");
+ if (result == NULL) {
+ result = data->stringWithCharset("iso-2022-jp");
+ }
+
+ return result;
+ }
+
+ result = stringWithCharset(charset->UTF8Characters());
+ return result;
+}
+
+String * Data::charsetWithFilteredHTMLWithoutHint(bool filterHTML)
+{
+ UCharsetDetector * detector;
+ const UCharsetMatch * match;
+ UErrorCode err = U_ZERO_ERROR;
+ const char * cName;
+ String * result;
+
+ detector = ucsdet_open(&err);
+ ucsdet_setText(detector, bytes(), length(), &err);
+ ucsdet_enableInputFilter(detector, filterHTML);
+ match = ucsdet_detect(detector, &err);
+ if (match == NULL) {
+ ucsdet_close(detector);
+ return NULL;
+ }
+
+ cName = ucsdet_getName(match, &err);
+
+ result = String::stringWithUTF8Characters(cName);
+ ucsdet_close(detector);
+
+ return result;
+}
+
+String * Data::charsetWithFilteredHTML(bool filterHTML, String * hintCharset)
+{
+ if (hintCharset == NULL)
+ return charsetWithFilteredHTMLWithoutHint(filterHTML);
+
+ const UCharsetMatch ** matches;
+ int32_t matchesCount;
+ UCharsetDetector * detector;
+ UErrorCode err = U_ZERO_ERROR;
+ String * result;
+
+ hintCharset = hintCharset->lowercaseString();
+
+ detector = ucsdet_open(&err);
+ ucsdet_setText(detector, bytes(), length(), &err);
+ ucsdet_enableInputFilter(detector, filterHTML);
+ matches = ucsdet_detectAll(detector, &matchesCount, &err);
+ if (matches == NULL) {
+ ucsdet_close(detector);
+ return hintCharset;
+ }
+ if (matchesCount == 0) {
+ ucsdet_close(detector);
+ return hintCharset;
+ }
+
+ result = NULL;
+
+ for(int32_t i = 0 ; i < matchesCount ; i ++) {
+ const char * cName;
+ String * name;
+ int32_t confidence;
+
+ cName = ucsdet_getName(matches[i], &err);
+ name = String::stringWithUTF8Characters(cName);
+ name = name->lowercaseString();
+ confidence = ucsdet_getConfidence(matches[i], &err);
+ if ((confidence >= 50) && name->isEqual(hintCharset)) {
+ result = name;
+ break;
+ }
+ }
+
+ if (result == NULL) {
+ int32_t maxConfidence;
+
+ maxConfidence = 49;
+
+ for(int32_t i = 0 ; i < matchesCount ; i ++) {
+ const char * cName;
+ String * name;
+ int32_t confidence;
+
+ cName = ucsdet_getName(matches[i], &err);
+ confidence = ucsdet_getConfidence(matches[i], &err);
+ name = String::stringWithUTF8Characters(cName);
+ if (confidence > maxConfidence) {
+ result = name;
+ maxConfidence = confidence;
+ }
+ }
+ }
+ ucsdet_close(detector);
+
+ if (result == NULL)
+ result = hintCharset;
+
+ return result;
+}
+
+Data * Data::dataWithContentsOfFile(String * filename)
+{
+ int r;
+ size_t read_items;
+ struct stat stat_buf;
+ FILE * f;
+ char * buf;
+ Data * data;
+
+ f = fopen(filename->fileSystemRepresentation(), "rb");
+ if (f == NULL) {
+ return NULL;
+ }
+
+ r = fstat(fileno(f), &stat_buf);
+ if (r < 0) {
+ fclose(f);
+ return NULL;
+ }
+
+ buf = (char *) malloc(stat_buf.st_size);
+
+ read_items = fread(buf, 1, stat_buf.st_size, f);
+ if ((off_t) read_items != stat_buf.st_size) {
+ free(buf);
+ fclose(f);
+ return NULL;
+ }
+
+ data = Data::dataWithBytes(buf, (unsigned int) stat_buf.st_size);
+ free(buf);
+
+ fclose(f);
+
+ return data;
+}
+
+static size_t uudecode(char * text, size_t size)
+{
+ unsigned int count = 0;
+ char *b = text; /* beg */
+ char *s = b; /* src */
+ char *d = b; /* dst */
+ char *e = b+size; /* end */
+ int out = (*s++ & 0x7f) - 0x20;
+
+ /* don't process lines without leading count character */
+ if (out < 0)
+ return size;
+
+ /* don't process begin and end lines */
+ if ((strncasecmp((const char *)b, "begin ", 6) == 0) ||
+ (strncasecmp((const char *)b, "end", 3) == 0))
+ return size;
+
+ //while (s < e - 4)
+ while (s < e)
+ {
+ int v = 0;
+ int i;
+ for (i = 0; i < 4; i += 1) {
+ char c = *s++;
+ v = v << 6 | ((c - 0x20) & 0x3F);
+ }
+ for (i = 2; i >= 0; i -= 1) {
+ char c = (char) (v & 0xFF);
+ d[i] = c;
+ v = v >> 8;
+ }
+ d += 3;
+ count += 3;
+ }
+ *d = (char) '\0';
+ return count;
+}
+
+Data * Data::decodedDataUsingEncoding(Encoding encoding)
+{
+ const char * text;
+ size_t text_length;
+
+ text = bytes();
+ text_length = length();
+
+ switch (encoding) {
+ case Encoding7Bit:
+ case Encoding8Bit:
+ case EncodingBinary:
+ case EncodingOther:
+ default:
+ {
+ return this;
+ }
+ case EncodingBase64:
+ case EncodingQuotedPrintable:
+ {
+ char * decoded;
+ size_t decoded_length;
+ size_t cur_token;
+ int mime_encoding;
+ Data * data;
+
+ switch (encoding) {
+ default: //disable warning
+ case EncodingBase64:
+ mime_encoding = MAILMIME_MECHANISM_BASE64;
+ break;
+ case EncodingQuotedPrintable:
+ mime_encoding = MAILMIME_MECHANISM_QUOTED_PRINTABLE;
+ break;
+ }
+
+ cur_token = 0;
+ mailmime_part_parse(text, text_length, &cur_token,
+ mime_encoding, &decoded, &decoded_length);
+ data = Data::dataWithBytes(decoded, (unsigned int) decoded_length);
+ mailmime_decoded_part_free(decoded);
+ return data;
+ }
+ case EncodingUUEncode:
+ {
+ char * dup_data;
+ size_t decoded_length;
+ Data * data;
+ char * current_p;
+
+ data = Data::dataWithCapacity((unsigned int) text_length);
+
+ dup_data = (char *) malloc(text_length);
+ memcpy(dup_data, text, text_length);
+
+ current_p = dup_data;
+ while (1) {
+ size_t length;
+ char * p;
+ char * p1;
+ char * p2;
+ char * end_line;
+
+ p1 = strchr(current_p, '\n');
+ p2 = strchr(current_p, '\r');
+ if (p1 == NULL) {
+ p = p2;
+ }
+ else if (p2 == NULL) {
+ p = p1;
+ }
+ else {
+ if (p1 - current_p < p2 - current_p) {
+ p = p1;
+ }
+ else {
+ p = p2;
+ }
+ }
+ end_line = p;
+ if (p != NULL) {
+ while ((size_t) (p - dup_data) < text_length) {
+ if ((* p != '\r') && (* p != '\n')) {
+ break;
+ }
+ p ++;
+ }
+ }
+ if (p == NULL) {
+ length = text_length - (current_p - dup_data);
+ }
+ else {
+ length = end_line - current_p;
+ }
+ if (length == 0) {
+ break;
+ }
+ decoded_length = uudecode(current_p, length);
+ if (decoded_length != 0 && decoded_length < length) {
+ data->appendBytes(current_p, (unsigned int) decoded_length);
+ }
+
+ if (p == NULL)
+ break;
+
+ current_p = p;
+ while ((size_t) (current_p - dup_data) < text_length) {
+ if ((* current_p != '\r') && (* current_p != '\n')) {
+ break;
+ }
+ current_p ++;
+ }
+ }
+ free(dup_data);
+
+ return data;
+ }
+ }
+}
+
+Data * Data::data()
+{
+ return dataWithCapacity(0);
+}
+
+Data * Data::dataWithCapacity(int capacity)
+{
+ Data * result = new Data(capacity);
+ return (Data *) result->autorelease();
+}
diff --git a/src/core/basetypes/MCData.h b/src/core/basetypes/MCData.h
new file mode 100644
index 00000000..52232383
--- /dev/null
+++ b/src/core/basetypes/MCData.h
@@ -0,0 +1,58 @@
+#ifndef __MAILCORE_MCDATA_H_
+
+#define __MAILCORE_MCDATA_H_
+
+#include <mailcore/MCObject.h>
+#include <mailcore/MCMessageConstants.h>
+
+namespace mailcore {
+
+ class String;
+
+ class Data : public Object {
+ private:
+ char * mBytes;
+ unsigned int mLength;
+ unsigned int mAllocated;
+ void allocate(unsigned int length);
+ void reset();
+ static String * normalizeCharset(String * charset);
+ String * charsetWithFilteredHTMLWithoutHint(bool filterHTML);
+
+ public:
+ Data();
+ Data(int capacity);
+ Data(Data * otherData);
+ Data(const char * bytes, unsigned int length);
+ virtual ~Data();
+
+ //virtual String * className();
+ virtual String * description();
+ virtual Object * copy();
+ virtual bool isEqual(Object * otherObject);
+ virtual unsigned int hash();
+
+ static Data * data();
+ static Data * dataWithCapacity(int capacity);
+ static Data * dataWithContentsOfFile(String * filename);
+ static Data * dataWithBytes(const char * bytes, unsigned int length);
+
+ virtual char * bytes();
+ virtual unsigned int length();
+
+ virtual void appendData(Data * otherData);
+ virtual void appendBytes(const char * bytes, unsigned int length);
+ virtual void setBytes(const char * bytes, unsigned int length);
+ virtual void setData(Data * otherData);
+
+ // Helpers
+ virtual String * stringWithDetectedCharset();
+ virtual String * emailStringWithCharset(String * charset);
+ virtual String * stringWithCharset(const char * charset);
+ virtual String * charsetWithFilteredHTML(bool filterHTML, String * hintCharset = NULL);
+ virtual Data * decodedDataUsingEncoding(Encoding encoding);
+ };
+
+}
+
+#endif
diff --git a/src/core/basetypes/MCHash.cc b/src/core/basetypes/MCHash.cc
new file mode 100644
index 00000000..116557e5
--- /dev/null
+++ b/src/core/basetypes/MCHash.cc
@@ -0,0 +1,12 @@
+#include "MCHash.h"
+
+unsigned int mailcore::hashCompute(const char * key, unsigned int len) {
+ register unsigned int c = 5381;
+ register const char * k = key;
+
+ while (len--) {
+ c = ((c << 5) + c) + *k++;
+ }
+
+ return c;
+}
diff --git a/src/core/basetypes/MCHash.h b/src/core/basetypes/MCHash.h
new file mode 100644
index 00000000..ecce9639
--- /dev/null
+++ b/src/core/basetypes/MCHash.h
@@ -0,0 +1,11 @@
+#ifndef __MAILCORE_MCHASH_H_
+
+#define __MAILCORE_MCHASH_H_
+
+namespace mailcore {
+
+ unsigned int hashCompute(const char * key, unsigned int len);
+
+}
+
+#endif
diff --git a/src/core/basetypes/MCHashMap.cc b/src/core/basetypes/MCHashMap.cc
new file mode 100644
index 00000000..8dcf0f30
--- /dev/null
+++ b/src/core/basetypes/MCHashMap.cc
@@ -0,0 +1,283 @@
+#include "MCHashMap.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "MCArray.h"
+#include "MCString.h"
+#include "MCUtils.h"
+#include "MCLog.h"
+
+using namespace mailcore;
+
+namespace mailcore {
+ struct HashMapCell {
+ unsigned int func;
+ Object * key;
+ Object * value;
+ HashMapCell * next;
+ };
+}
+
+#define CHASH_DEFAULTSIZE 13
+#define CHASH_MAXDEPTH 3
+
+void HashMap::init()
+{
+ mCount = 0;
+ mCells = (void **) (HashMapCell **) calloc(CHASH_DEFAULTSIZE, sizeof(HashMapCell *));
+ mAllocated = CHASH_DEFAULTSIZE;
+}
+
+HashMap::HashMap()
+{
+ init();
+}
+
+HashMap::HashMap(HashMap * other)
+{
+ init();
+ Array * keys = other->allKeys();
+ for(unsigned int i = 0 ; i < keys->count() ; i ++) {
+ Object * key = keys->objectAtIndex(i);
+ Object * value = other->objectForKey(key);
+ setObjectForKey(key, value);
+ }
+}
+
+HashMap::~HashMap()
+{
+ for(unsigned int indx = 0; indx < mAllocated; indx++) {
+ HashMapIter * iter, * next;
+ iter = (HashMapIter *) mCells[indx];
+ while (iter) {
+ next = iter->next;
+ iter->key->release();
+ iter->value->release();
+ free(iter);
+ iter = next;
+ }
+ }
+ free(mCells);
+}
+
+void HashMap::allocate(unsigned int size)
+{
+ HashMapCell ** cells;
+ unsigned int indx, nindx;
+ HashMapIter * iter, * next;
+
+ if (mAllocated == size)
+ return;
+
+ cells = (HashMapCell **) calloc(size, sizeof(HashMapCell *));
+ /* iterate over initial hash and copy items in second hash */
+ for(indx = 0 ; indx < mAllocated ; indx ++) {
+ iter = (HashMapIter *) mCells[indx];
+ while (iter) {
+ next = iter->next;
+ nindx = iter->func % size;
+ iter->next = cells[nindx];
+ cells[nindx] = iter;
+ iter = next;
+ }
+ }
+ free(mCells);
+ mAllocated = size;
+ mCells = (void **) cells;
+}
+
+HashMap * HashMap::hashMap()
+{
+ HashMap * result = new HashMap();
+ return (HashMap *) result->autorelease();
+}
+
+#if 0
+String * HashMap::className()
+{
+ return MCSTR("HashMap");
+}
+#endif
+
+String * HashMap::description()
+{
+ String * result = String::string();
+ Array * keys = allKeys();
+ result->appendUTF8Characters("{");
+ for(unsigned int i = 0 ; i < keys->count() ; i ++) {
+ Object * key = keys->objectAtIndex(i);
+ if (i != 0) {
+ result->appendUTF8Characters(",");
+ }
+ result->appendString(key->description());
+ result->appendUTF8Characters(":");
+ Object * value = objectForKey(key);
+ result->appendString(value->description());
+ }
+ result->appendUTF8Characters("}");
+
+ return result;
+}
+
+Object * HashMap::copy()
+{
+ return new HashMap(this);
+}
+
+unsigned int HashMap::count()
+{
+ return mCount;
+}
+
+void HashMap::setObjectForKey(Object * key, Object * value)
+{
+ unsigned int func, indx;
+ HashMapIter * iter, * cell;
+
+ if (mCount > mAllocated * CHASH_MAXDEPTH) {
+ allocate((mCount / CHASH_MAXDEPTH) * 2 + 1);
+ }
+
+ func = key->hash();
+ indx = func % mAllocated;
+
+ /* look for the key in existing cells */
+ iter = (HashMapIter *) mCells[indx];
+ while (iter) {
+ if (iter->func == func && iter->key->isEqual(key)) {
+ /* found, replacing entry */
+ value->retain();
+ iter->value->release();
+ iter->value = value;
+ return;
+ }
+ iter = iter->next;
+ }
+
+ /* not found, adding entry */
+ cell = (HashMapCell *) malloc(sizeof(HashMapCell));
+ cell->key = key->copy();
+ cell->value = value->retain();
+ cell->func = func;
+ cell->next = (HashMapCell *) mCells[indx];
+ mCells[indx] = cell;
+ mCount ++;
+}
+
+void HashMap::removeObjectForKey(Object * key)
+{
+ unsigned int func, indx;
+ HashMapIter * iter, * old;
+
+ func = key->hash();;
+ indx = func % mAllocated;
+
+ /* look for the key in existing cells */
+ old = NULL;
+ iter = (HashMapIter *) mCells[indx];
+ while (iter) {
+ if (iter->func == func && iter->key->isEqual(key)) {
+ /* found, deleting */
+ if (old)
+ old->next = iter->next;
+ else
+ mCells[indx] = iter->next;
+ iter->key->release();
+ iter->value->release();
+ free(iter);
+ mCount --;
+ return;
+ }
+ old = iter;
+ iter = iter->next;
+ }
+ // Not found.
+}
+
+Object * HashMap::objectForKey(Object * key)
+{
+ unsigned int func;
+ HashMapIter * iter;
+
+ func = key->hash();
+
+ /* look for the key in existing cells */
+ iter = (HashMapIter *) mCells[func % mAllocated];
+ while (iter) {
+ if (iter->func == func && key->isEqual(iter->key)) {
+ return iter->value; /* found */
+ }
+ iter = iter->next;
+ }
+ return NULL;
+}
+
+HashMapIter * HashMap::iteratorBegin()
+{
+ HashMapIter * iter;
+ unsigned int indx = 0;
+
+ iter = (HashMapIter *) mCells[0];
+ while (!iter) {
+ indx ++;
+ if (indx >= mAllocated)
+ return NULL;
+ iter = (HashMapIter *) mCells[indx];
+ }
+ return iter;
+}
+
+HashMapIter * HashMap::iteratorNext(HashMapIter * iter)
+{
+ unsigned int indx;
+
+ if (!iter)
+ return NULL;
+
+ indx = iter->func % mAllocated;
+ iter = iter->next;
+
+ while(!iter) {
+ indx++;
+ if (indx >= mAllocated)
+ return NULL;
+ iter = (HashMapIter *) mCells[indx];
+ }
+ return iter;
+}
+
+Array * HashMap::allKeys()
+{
+ Array * keys = Array::array();
+ for(HashMapIter * iter = iteratorBegin() ; iter != NULL ; iter = iteratorNext(iter)) {
+ keys->addObject(iter->key);
+ }
+ return keys;
+}
+
+Array * HashMap::allValues()
+{
+ Array * values = Array::array();
+ for(HashMapIter * iter = iteratorBegin() ; iter != NULL ; iter = iteratorNext(iter)) {
+ values->addObject(iter->value);
+ }
+ return values;
+}
+
+void HashMap::removeAllObjects()
+{
+ for(unsigned int indx = 0 ; indx < mAllocated ; indx++) {
+ HashMapIter * iter, * next;
+ iter = (HashMapIter *) mCells[indx];
+ while (iter) {
+ next = iter->next;
+ iter->key->release();
+ iter->value->release();
+ free(iter);
+ iter = next;
+ }
+ }
+ memset(mCells, 0, mAllocated * sizeof(* mCells));
+ mCount = 0;
+}
diff --git a/src/core/basetypes/MCHashMap.h b/src/core/basetypes/MCHashMap.h
new file mode 100644
index 00000000..99e8db52
--- /dev/null
+++ b/src/core/basetypes/MCHashMap.h
@@ -0,0 +1,45 @@
+#ifndef __MAILCORE_MCHASHMAP_H_
+
+#define __MAILCORE_MCHASHMAP_H_
+
+#include <mailcore/MCObject.h>
+
+namespace mailcore {
+
+ class String;
+ class Array;
+ struct HashMapCell;
+ typedef HashMapCell HashMapIter;
+
+ class HashMap : public Object {
+ private:
+ unsigned int mAllocated;
+ unsigned int mCount;
+ void ** mCells;
+ HashMapIter * iteratorBegin();
+ HashMapIter * iteratorNext(HashMapIter * iter);
+ void allocate(unsigned int size);
+ void init();
+ public:
+ HashMap();
+ HashMap(HashMap * o);
+ virtual ~HashMap();
+
+ static HashMap * hashMap();
+
+ //virtual String * className();
+ virtual String * description();
+ virtual Object * copy();
+
+ virtual unsigned int count();
+ virtual void setObjectForKey(Object * key, Object * value);
+ virtual void removeObjectForKey(Object * key);
+ virtual Object * objectForKey(Object * key);
+ virtual Array * allKeys();
+ virtual Array * allValues();
+ virtual void removeAllObjects();
+ };
+
+}
+
+#endif
diff --git a/src/core/basetypes/MCLog.cc b/src/core/basetypes/MCLog.cc
new file mode 100644
index 00000000..2ceceecc
--- /dev/null
+++ b/src/core/basetypes/MCLog.cc
@@ -0,0 +1,30 @@
+#include "MCLog.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+static void logInternalv(FILE * file,
+ const char * user, const char * filename, unsigned int line,
+ int dumpStack, const char * format, va_list argp);
+
+void mailcore::logInternal(const char * user,
+ const char * filename,
+ unsigned int line,
+ int dumpStack,
+ const char * format, ...)
+{
+ va_list argp;
+
+ va_start(argp, format);
+ logInternalv(stderr, user, filename, line, dumpStack, format, argp);
+ va_end(argp);
+}
+
+static void logInternalv(FILE * file,
+ const char * user, const char * filename, unsigned int line,
+ int dumpStack, const char * format, va_list argp)
+{
+ fprintf(file, "%s:%i: ", filename, line);
+ vfprintf(file, format, argp);
+ fprintf(file, "\n");
+}
diff --git a/src/core/basetypes/MCLog.h b/src/core/basetypes/MCLog.h
new file mode 100644
index 00000000..42246e61
--- /dev/null
+++ b/src/core/basetypes/MCLog.h
@@ -0,0 +1,19 @@
+#ifndef __MAILCORE_MCLOG_H_
+
+#define __MAILCORE_MCLOG_H_
+
+#include <stdio.h>
+
+#define MCLog(...) mailcore::logInternal(NULL, __FILE__, __LINE__, 0, __VA_ARGS__)
+
+namespace mailcore {
+
+ void logInternal(const char * user,
+ const char * filename,
+ unsigned int line,
+ int dumpStack,
+ const char * format, ...) __printflike(5, 6);
+
+}
+
+#endif
diff --git a/src/core/basetypes/MCMainThread.h b/src/core/basetypes/MCMainThread.h
new file mode 100644
index 00000000..db8e1141
--- /dev/null
+++ b/src/core/basetypes/MCMainThread.h
@@ -0,0 +1,11 @@
+#ifndef __MAILCORE_MCMAINTHREAD_H
+
+#define __MAILCORE_MCMAINTHREAD_H
+
+namespace mailcore {
+ void callOnMainThread(void (*)(void *), void * context);
+ void callOnMainThreadAndWait(void (*)(void *), void * context);
+ void callAfterDelay(void (*)(void *), void * context, double time);
+}
+
+#endif
diff --git a/src/core/basetypes/MCMainThread.mm b/src/core/basetypes/MCMainThread.mm
new file mode 100644
index 00000000..31d83b5e
--- /dev/null
+++ b/src/core/basetypes/MCMainThread.mm
@@ -0,0 +1,60 @@
+#include "MCMainThread.h"
+
+#import <Foundation/Foundation.h>
+
+using namespace mailcore;
+
+@interface LEPPPMainThreadCaller : NSObject {
+ void (* _function)(void *);
+ void * _context;
+}
+
+@property (nonatomic, assign) void (* function)(void *);
+@property (nonatomic, assign) void * context;
+
+- (void) call;
+
+@end
+
+@implementation LEPPPMainThreadCaller
+
+@synthesize function = _function;
+@synthesize context = _context;
+
+- (void) call
+{
+ _function(_context);
+}
+
+@end
+
+void mailcore::callOnMainThread(void (* function)(void *), void * context)
+{
+ LEPPPMainThreadCaller * caller;
+ caller = [[LEPPPMainThreadCaller alloc] init];
+ [caller setFunction:function];
+ [caller setContext:context];
+ [caller performSelectorOnMainThread:@selector(call) withObject:nil waitUntilDone:NO];
+ [caller release];
+}
+
+void mailcore::callOnMainThreadAndWait(void (* function)(void *), void * context)
+{
+ LEPPPMainThreadCaller * caller;
+ caller = [[LEPPPMainThreadCaller alloc] init];
+ [caller setFunction:function];
+ [caller setContext:context];
+ [caller performSelectorOnMainThread:@selector(call) withObject:nil waitUntilDone:YES];
+ [caller release];
+}
+
+void mailcore::callAfterDelay(void (* function)(void *), void * context, double time)
+{
+ LEPPPMainThreadCaller * caller;
+ caller = [[LEPPPMainThreadCaller alloc] init];
+ [caller setFunction:function];
+ [caller setContext:context];
+ [caller performSelector:@selector(call) withObject:nil afterDelay:time];
+ [caller release];
+}
+
diff --git a/src/core/basetypes/MCObject.cc b/src/core/basetypes/MCObject.cc
new file mode 100644
index 00000000..220a5a16
--- /dev/null
+++ b/src/core/basetypes/MCObject.cc
@@ -0,0 +1,180 @@
+#include "MCObject.h"
+
+#include <stdlib.h>
+#include <typeinfo>
+#include <cxxabi.h>
+
+#include "MCAutoreleasePool.h"
+#include "MCString.h"
+#include "MCHash.h"
+#include "MCLog.h"
+#include "MCUtils.h"
+#include "MCAssert.h"
+#include "MCMainThread.h"
+
+using namespace mailcore;
+
+Object::Object()
+{
+ init();
+}
+
+Object::~Object()
+{
+}
+
+void Object::init()
+{
+ pthread_mutex_init(&mLock, NULL);
+ mCounter = 1;
+}
+
+int Object::retainCount()
+{
+ pthread_mutex_lock(&mLock);
+ int value = mCounter;
+ pthread_mutex_unlock(&mLock);
+
+ return value;
+}
+
+Object * Object::retain()
+{
+ pthread_mutex_lock(&mLock);
+ mCounter ++;
+ pthread_mutex_unlock(&mLock);
+ return this;
+}
+
+void Object::release()
+{
+ bool shouldRelease = false;
+
+ pthread_mutex_lock(&mLock);
+ mCounter --;
+ if (mCounter == 0) {
+ shouldRelease = true;
+ }
+ pthread_mutex_unlock(&mLock);
+
+ if (shouldRelease) {
+ //MCLog("dealloc %s", className()->description()->UTF8Characters());
+ delete this;
+ }
+}
+
+Object * Object::autorelease()
+{
+ AutoreleasePool::autorelease(this);
+ return this;
+}
+
+String * Object::className()
+{
+ int status;
+ char * unmangled = abi::__cxa_demangle(typeid(* this).name(), NULL, NULL, &status);
+ //return mailcore::String::uniquedStringWithUTF8Characters(typeid(* this).name());
+ return mailcore::String::uniquedStringWithUTF8Characters(unmangled);
+}
+
+/*
+String * Object::className()
+{
+ return MCSTR("Object");
+}
+*/
+
+String * Object::description()
+{
+ return String::stringWithUTF8Format("<%s:%p>", className()->UTF8Characters(), this);
+}
+
+bool Object::isEqual(Object * otherObject)
+{
+ return this == otherObject;
+}
+
+unsigned int Object::hash()
+{
+ return hashCompute((const char *) this, sizeof(this));
+}
+
+Object * Object::copy()
+{
+ MCAssert(0);
+ return NULL;
+}
+
+void Object::performMethod(Object::Method method, void * context)
+{
+ (this->*method)(context);
+}
+
+struct mainThreadCallData {
+ Object * obj;
+ void * context;
+ Object::Method method;
+};
+
+static void performOnMainThread(void * info)
+{
+ struct mainThreadCallData * data;
+ void * context;
+ Object * obj;
+ Object::Method method;
+
+ data = (struct mainThreadCallData *) info;
+ obj = data->obj;
+ context = data->context;
+ method = data->method;
+
+ (obj->*method)(context);
+
+ free(data);
+}
+
+static void callAfterDelay(void * info)
+{
+ struct mainThreadCallData * data;
+ void * context;
+ Object * obj;
+ Object::Method method;
+
+ data = (struct mainThreadCallData *) info;
+ obj = data->obj;
+ context = data->context;
+ method = data->method;
+
+ (obj->*method)(context);
+
+ free(data);
+}
+
+void Object::performMethodOnMainThread(Method method, void * context, bool waitUntilDone)
+{
+ struct mainThreadCallData * data;
+
+ data = (struct mainThreadCallData *) calloc(sizeof(* data), 1);
+ data->obj = this;
+ data->context = context;
+ data->method = method;
+
+ if (waitUntilDone) {
+ callOnMainThreadAndWait(performOnMainThread, data);
+ }
+ else {
+ callOnMainThread(performOnMainThread, data);
+ }
+}
+
+void Object::performMethodAfterDelay(Method method, void * context, double delay)
+{
+ struct mainThreadCallData * data;
+
+ data = (struct mainThreadCallData *) calloc(sizeof(* data), 1);
+ data->obj = this;
+ data->context = context;
+ data->method = method;
+
+ callAfterDelay(performOnMainThread, data, delay);
+}
diff --git a/src/core/basetypes/MCObject.h b/src/core/basetypes/MCObject.h
new file mode 100644
index 00000000..9e323ebf
--- /dev/null
+++ b/src/core/basetypes/MCObject.h
@@ -0,0 +1,41 @@
+#ifndef __MAILCORE_MCOBJECT_H_
+
+#define __MAILCORE_MCOBJECT_H_
+
+#include <pthread.h>
+
+namespace mailcore {
+
+ class String;
+
+ class Object {
+ private:
+ pthread_mutex_t mLock;
+ int mCounter;
+ void init();
+ public:
+ Object();
+ virtual ~Object();
+
+ virtual int retainCount();
+ virtual Object * retain();
+ virtual void release();
+ virtual Object * autorelease();
+ virtual String * description();
+ virtual String * className();
+
+ virtual bool isEqual(Object * otherObject);
+ virtual unsigned int hash();
+
+ // optional
+ virtual Object * copy();
+
+ typedef void (Object::*Method) (void *);
+ virtual void performMethod(Method method, void * context);
+ virtual void performMethodOnMainThread(Method method, void * context, bool waitUntilDone = false);
+ virtual void performMethodAfterDelay(Method method, void * context, double delay);
+ };
+
+}
+
+#endif
diff --git a/src/core/basetypes/MCOperation.cc b/src/core/basetypes/MCOperation.cc
new file mode 100644
index 00000000..61dd48e6
--- /dev/null
+++ b/src/core/basetypes/MCOperation.cc
@@ -0,0 +1,45 @@
+#include "MCOperation.h"
+
+using namespace mailcore;
+
+Operation::Operation()
+{
+ mCallback = NULL;
+ mCancelled = false;
+ pthread_mutex_init(&mLock, NULL);
+}
+
+Operation::~Operation()
+{
+ pthread_mutex_destroy(&mLock);
+}
+
+void Operation::setCallback(OperationCallback * callback)
+{
+ mCallback = callback;
+}
+
+OperationCallback * Operation::callback()
+{
+ return mCallback;
+}
+
+void Operation::cancel()
+{
+ pthread_mutex_lock(&mLock);
+ mCancelled = true;
+ pthread_mutex_unlock(&mLock);
+}
+
+bool Operation::isCancelled()
+{
+ pthread_mutex_lock(&mLock);
+ bool value = mCancelled;
+ pthread_mutex_unlock(&mLock);
+
+ return value;
+}
+
+void Operation::main()
+{
+}
diff --git a/src/core/basetypes/MCOperation.h b/src/core/basetypes/MCOperation.h
new file mode 100644
index 00000000..80bd8d30
--- /dev/null
+++ b/src/core/basetypes/MCOperation.h
@@ -0,0 +1,33 @@
+#ifndef __MAILCORE_MCOPERATION_H_
+
+#define __MAILCORE_MCOPERATION_H_
+
+#include <pthread.h>
+#include <mailcore/MCObject.h>
+
+namespace mailcore {
+
+ class OperationCallback;
+
+ class Operation : public Object {
+ private:
+ OperationCallback * mCallback;
+ bool mCancelled;
+ pthread_mutex_t mLock;
+
+ public:
+ Operation();
+ virtual ~Operation();
+
+ virtual void setCallback(OperationCallback * callback);
+ virtual OperationCallback * callback();
+
+ virtual void cancel();
+ virtual bool isCancelled();
+
+ virtual void main();
+ };
+
+}
+
+#endif
diff --git a/src/core/basetypes/MCOperationCallback.h b/src/core/basetypes/MCOperationCallback.h
new file mode 100644
index 00000000..e9623c76
--- /dev/null
+++ b/src/core/basetypes/MCOperationCallback.h
@@ -0,0 +1,16 @@
+#ifndef __MAILCORE_MCOPERATIONCALLBACK_H_
+
+#define __MAILCORE_MCOPERATIONCALLBACK_H_
+
+namespace mailcore {
+
+ class Operation;
+
+ class OperationCallback {
+ public:
+ virtual void operationFinished(Operation * op) {}
+ };
+
+}
+
+#endif
diff --git a/src/core/basetypes/MCOperationQueue.cc b/src/core/basetypes/MCOperationQueue.cc
new file mode 100644
index 00000000..3c22b89c
--- /dev/null
+++ b/src/core/basetypes/MCOperationQueue.cc
@@ -0,0 +1,179 @@
+#include "MCOperationQueue.h"
+
+#include "MCOperation.h"
+#include "MCOperationCallback.h"
+#include "MCMainThread.h"
+#include "MCUtils.h"
+#include "MCArray.h"
+#include "MCLog.h"
+#include <libetpan/libetpan.h>
+
+using namespace mailcore;
+
+OperationQueue::OperationQueue()
+{
+ mOperations = new Array();
+ mStarted = false;
+ //sem_init(&mOperationSem, 0, 0);
+ //sem_init(&mStartSem, 0, 0);
+ //sem_init(&mStopSem, 0, 0);
+ pthread_mutex_init(&mLock, NULL);
+ mWaiting = false;
+ //sem_init(&mWaitingFinishedSem, 0, 0);
+ mOperationSem = mailsem_new();
+ mStartSem = mailsem_new();
+ mStopSem = mailsem_new();
+ mWaitingFinishedSem = mailsem_new();
+}
+
+OperationQueue::~OperationQueue()
+{
+ MC_SAFE_RELEASE(mOperations);
+ //sem_destroy(&mOperationSem);
+ //sem_destroy(&mStartSem);
+ //sem_destroy(&mStopSem);
+ pthread_mutex_destroy(&mLock);
+ //sem_destroy(&mWaitingFinishedSem);
+ mailsem_free(mOperationSem);
+ mailsem_free(mStartSem);
+ mailsem_free(mStopSem);
+ mailsem_free(mWaitingFinishedSem);
+}
+
+void OperationQueue::addOperation(Operation * op)
+{
+ pthread_mutex_lock(&mLock);
+ mOperations->addObject(op);
+ pthread_mutex_unlock(&mLock);
+ //sem_post(&mOperationSem);
+ mailsem_up(mOperationSem);
+ startThread();
+}
+
+void OperationQueue::runOperationsOnThread(OperationQueue * queue)
+{
+ queue->runOperations();
+}
+
+void OperationQueue::runOperations()
+{
+ MCLog("start thread");
+ //sem_post(&mStartSem);
+ mailsem_up(mStartSem);
+
+ while (true) {
+ Operation * op = NULL;
+ bool needsCheckRunning = false;
+
+ //int value = 0;
+ //int r;
+
+ //r = sem_getvalue(&mOperationSem, &value);
+ //MCLog("x before sem %i %i", value, r);
+ //sem_wait(&mOperationSem);
+ mailsem_down(mOperationSem);
+ //sem_getvalue(&mOperationSem, &value);
+ //MCLog("x after sem %i", value);
+
+ pthread_mutex_lock(&mLock);
+ if (mOperations->count() > 0) {
+ op = (Operation *) mOperations->objectAtIndex(0);
+ }
+ pthread_mutex_unlock(&mLock);
+
+ if (op == NULL) {
+ //sem_post(&mStopSem);
+ mailsem_up(mStopSem);
+ break;
+ }
+
+ op->main();
+
+ if (op->callback() != NULL) {
+ performMethodOnMainThread((Object::Method) &OperationQueue::callbackOnMainThread, op, true);
+ }
+
+ pthread_mutex_lock(&mLock);
+ mOperations->removeObjectAtIndex(0);
+ if (mOperations->count() == 0) {
+ if (mWaiting) {
+ //sem_post(&mWaitingFinishedSem);
+ mailsem_up(mWaitingFinishedSem);
+ }
+ needsCheckRunning = true;
+ }
+ pthread_mutex_unlock(&mLock);
+
+ if (needsCheckRunning) {
+ retain(); // (1)
+ performMethodOnMainThread((Object::Method) &OperationQueue::checkRunningOnMainThread, this);
+ }
+ }
+ MCLog("cleanup thread");
+}
+
+void OperationQueue::callbackOnMainThread(Operation * op)
+{
+ if (op->callback() != NULL) {
+ op->callback()->operationFinished(op);
+ }
+}
+
+void OperationQueue::checkRunningOnMainThread(void * context)
+{
+ performMethodAfterDelay((Object::Method) &OperationQueue::checkRunningAfterDelay, NULL, 1);
+}
+
+void OperationQueue::checkRunningAfterDelay(void * context)
+{
+ bool quitting = false;
+
+ pthread_mutex_lock(&mLock);
+ if (mOperations->count() == 0) {
+ //sem_post(&mOperationSem);
+ mailsem_up(mOperationSem);
+ quitting = true;
+ }
+ pthread_mutex_unlock(&mLock);
+
+ // Number of operations can't be changed because it runs on main thread.
+ // And addOperation() should also be called from main thread.
+
+ if (quitting) {
+ //sem_wait(&mStopSem);
+ mailsem_down(mStopSem);
+ mStarted = false;
+ }
+
+ release(); // (1)
+}
+
+void OperationQueue::startThread()
+{
+ if (mStarted)
+ return;
+
+ mStarted = true;
+ pthread_create(&mThreadID, NULL, (void * (*)(void *)) OperationQueue::runOperationsOnThread, this);
+ //sem_wait(&mStartSem);
+ mailsem_down(mStartSem);
+}
+
+#if 0
+void OperationQueue::waitUntilAllOperationsAreFinished()
+{
+ bool waiting = false;
+
+ pthread_mutex_lock(&mLock);
+ if (mOperations->count() > 0) {
+ mWaiting = true;
+ waiting = true;
+ }
+ pthread_mutex_unlock(&mLock);
+
+ if (waiting) {
+ sem_wait(&mWaitingFinishedSem);
+ }
+ mWaiting = false;
+}
+#endif
diff --git a/src/core/basetypes/MCOperationQueue.h b/src/core/basetypes/MCOperationQueue.h
new file mode 100644
index 00000000..b858aebc
--- /dev/null
+++ b/src/core/basetypes/MCOperationQueue.h
@@ -0,0 +1,45 @@
+#ifndef __MAILCORE_MCOPERATIONQUEUE_H_
+
+#define __MAILCORE_MCOPERATIONQUEUE_H_
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <mailcore/MCObject.h>
+#include <libetpan/libetpan.h>
+
+namespace mailcore {
+
+ class Operation;
+ class Array;
+
+ class OperationQueue : public Object {
+ private:
+ Array * mOperations;
+ pthread_t mThreadID;
+ bool mStarted;
+ struct mailsem * mOperationSem;
+ struct mailsem * mStartSem;
+ struct mailsem * mStopSem;
+ pthread_mutex_t mLock;
+ bool mWaiting;
+ struct mailsem * mWaitingFinishedSem;
+
+ void startThread();
+ static void runOperationsOnThread(OperationQueue * queue);
+ void runOperations();
+ void callbackOnMainThread(Operation * op);
+ void checkRunningOnMainThread(void * context);
+ void checkRunningAfterDelay(void * context);
+
+ public:
+ OperationQueue();
+ virtual ~OperationQueue();
+
+ virtual void addOperation(Operation * op);
+
+ //virtual void waitUntilAllOperationsAreFinished();
+ };
+
+}
+
+#endif
diff --git a/src/core/basetypes/MCRange.cc b/src/core/basetypes/MCRange.cc
new file mode 100644
index 00000000..ead5c189
--- /dev/null
+++ b/src/core/basetypes/MCRange.cc
@@ -0,0 +1,12 @@
+#include "MCRange.h"
+
+using namespace mailcore;
+
+Range mailcore::RangeMake(unsigned int index, unsigned int length)
+{
+ Range range;
+ range.index = index;
+ range.length = length;
+ return range;
+}
+
diff --git a/src/core/basetypes/MCRange.h b/src/core/basetypes/MCRange.h
new file mode 100644
index 00000000..752c4bec
--- /dev/null
+++ b/src/core/basetypes/MCRange.h
@@ -0,0 +1,15 @@
+#ifndef __MAILCORE_MCRANGE_H_
+
+#define __MAILCORE_MCRANGE_H_
+
+namespace mailcore {
+
+ struct Range {
+ unsigned int index;
+ unsigned int length;
+ };
+
+ Range RangeMake(unsigned int index, unsigned int length);
+}
+
+#endif
diff --git a/src/core/basetypes/MCSet.cc b/src/core/basetypes/MCSet.cc
new file mode 100644
index 00000000..fcda7f62
--- /dev/null
+++ b/src/core/basetypes/MCSet.cc
@@ -0,0 +1,106 @@
+#include "MCSet.h"
+
+#include "MCHashMap.h"
+#include "MCString.h"
+#include "MCUtils.h"
+#include "MCArray.h"
+#include "MCLog.h"
+
+using namespace mailcore;
+
+void Set::init()
+{
+ mHash = new HashMap();
+}
+
+Set::Set()
+{
+ init();
+}
+
+Set::Set(Set * o)
+{
+ init();
+ MC_SAFE_REPLACE_COPY(HashMap, mHash, o->mHash);
+}
+
+Set::~Set()
+{
+ delete mHash;
+}
+
+Set * Set::set()
+{
+ Set * result = new Set();
+ return (Set *) result->autorelease();
+}
+
+Set * Set::setWithArray(Array * objects)
+{
+ Set * result = new Set();
+ result->addObjectsFromArray(objects);
+ return (Set *) result->autorelease();
+}
+
+#if 0
+String * Set::className()
+{
+ return MCSTR("Set");
+}
+#endif
+
+String * Set::description()
+{
+ String * result = String::string();
+ result->appendUTF8Format("<%s:%p ", className(), this);
+ result->appendString(mHash->allKeys()->description());
+ result->appendUTF8Characters(">");
+ return result;
+}
+
+Object * Set::copy()
+{
+ return new Set(this);
+}
+
+unsigned int Set::count()
+{
+ return mHash->count();
+}
+
+void Set::addObject(Object * obj)
+{
+ mHash->setObjectForKey(obj, obj);
+}
+
+void Set::removeObject(Object * obj)
+{
+ mHash->removeObjectForKey(obj);
+}
+
+Object * Set::member(Object * obj)
+{
+ return mHash->objectForKey(obj);
+}
+
+bool Set::containsObject(Object * obj)
+{
+ return (mHash->objectForKey(obj) != NULL);
+}
+
+Array * Set::allObjects()
+{
+ return mHash->allKeys();
+}
+
+void Set::removeAllObjects()
+{
+ mHash->removeAllObjects();
+}
+
+void Set::addObjectsFromArray(Array * objects)
+{
+ for(unsigned int i= 0 ; i < objects->count() ; i ++) {
+ addObject(objects->objectAtIndex(i));
+ }
+}
diff --git a/src/core/basetypes/MCSet.h b/src/core/basetypes/MCSet.h
new file mode 100644
index 00000000..0f1ab360
--- /dev/null
+++ b/src/core/basetypes/MCSet.h
@@ -0,0 +1,42 @@
+#ifndef __MAILCORE_CSET_H_
+
+#define __MAILCORE_CSET_H_
+
+#include <mailcore/MCObject.h>
+
+namespace mailcore {
+
+ class String;
+ class Array;
+ class HashMap;
+
+ class Set : public Object {
+ private:
+ HashMap * mHash;
+ void init();
+ public:
+ Set();
+ Set(Set * o);
+ virtual ~Set();
+
+ static Set * set();
+ static Set * setWithArray(Array * objects);
+
+ //virtual String * className();
+ virtual String * description();
+ virtual Object * copy();
+
+ virtual unsigned int count();
+ virtual void addObject(Object * obj);
+ virtual void removeObject(Object * obj);
+ virtual bool containsObject(Object * obj);
+ virtual Object * member(Object * obj);
+
+ virtual Array * allObjects();
+ virtual void removeAllObjects();
+ virtual void addObjectsFromArray(Array * objects);
+ };
+
+}
+
+#endif
diff --git a/src/core/basetypes/MCString.cc b/src/core/basetypes/MCString.cc
new file mode 100644
index 00000000..5f6948d6
--- /dev/null
+++ b/src/core/basetypes/MCString.cc
@@ -0,0 +1,1958 @@
+#include "MCString.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <unicode/ustring.h>
+#include <unicode/ucnv.h>
+#include <uuid/uuid.h>
+#include <pthread.h>
+#include <libetpan/libetpan.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/HTMLparser.h>
+
+#include "MCData.h"
+#include "MCHash.h"
+#include "MCLog.h"
+#include "MCUtils.h"
+#include "MCRange.h"
+#include "MCArray.h"
+#include "MCSet.h"
+#include "MCHashMap.h"
+#include "MCAutoreleasePool.h"
+#include "MCValue.h"
+
+using namespace mailcore;
+
+#pragma mark quote headers string
+
+static inline int to_be_quoted(const char * word, size_t size, int subject)
+{
+ int do_quote;
+ const char * cur;
+ size_t i;
+
+ do_quote = 0;
+ cur = word;
+ for(i = 0 ; i < size ; i ++) {
+ if (* cur == '=')
+ do_quote = 1;
+
+ if (!subject) {
+ switch (* cur) {
+ case ',':
+ case ':':
+ case '!':
+ case '"':
+ case '#':
+ case '$':
+ case '@':
+ case '[':
+ case '\\':
+ case ']':
+ case '^':
+ case '`':
+ case '{':
+ case '|':
+ case '}':
+ case '~':
+ case '=':
+ case '?':
+ case '_':
+ do_quote = 1;
+ break;
+ }
+ }
+ if (((unsigned char) * cur) >= 128)
+ do_quote = 1;
+
+ cur ++;
+ }
+
+ return do_quote;
+}
+
+#define MAX_IMF_LINE 72
+
+static inline void quote_word(const char * display_charset,
+ MMAPString * mmapstr, const char * word, size_t size)
+{
+ const char * cur;
+ size_t i;
+ char hex[4];
+ int col;
+
+ mmap_string_append(mmapstr, "=?");
+ mmap_string_append(mmapstr, display_charset);
+ mmap_string_append(mmapstr, "?Q?");
+
+ col = (int) mmapstr->len;
+
+ cur = word;
+ for(i = 0 ; i < size ; i ++) {
+ int do_quote_char;
+
+ do_quote_char = 0;
+ switch (* cur) {
+ case ',':
+ case ':':
+ case '!':
+ case '"':
+ case '#':
+ case '$':
+ case '@':
+ case '[':
+ case '\\':
+ case ']':
+ case '^':
+ case '`':
+ case '{':
+ case '|':
+ case '}':
+ case '~':
+ case '=':
+ case '?':
+ case '_':
+ do_quote_char = 1;
+ break;
+
+ default:
+ if (((unsigned char) * cur) >= 128)
+ do_quote_char = 1;
+ break;
+ }
+
+ if (do_quote_char) {
+ snprintf(hex, 4, "=%2.2X", (unsigned char) * cur);
+ mmap_string_append(mmapstr, hex);
+ col += 3;
+ }
+ else {
+ if (* cur == ' ') {
+ mmap_string_append_c(mmapstr, '_');
+ }
+ else {
+ mmap_string_append_c(mmapstr, * cur);
+ }
+ col += 3;
+ }
+ cur ++;
+ }
+
+ mmap_string_append(mmapstr, "?=");
+}
+
+static inline void get_word(const char * begin, const char ** pend, int subject, int * pto_be_quoted)
+{
+ const char * cur;
+
+ cur = begin;
+
+ while ((* cur != ' ') && (* cur != '\t') && (* cur != '\0')) {
+ cur ++;
+ }
+ while (((* cur == ' ') || (* cur == '\t')) && (* cur != '\0')) {
+ cur ++;
+ }
+
+ if (cur - begin +
+ 1 /* minimum column of string in a
+ folded header */ > MAX_IMF_LINE)
+ * pto_be_quoted = 1;
+ else
+ * pto_be_quoted = to_be_quoted(begin, cur - begin, subject);
+
+ * pend = cur;
+}
+
+static char * etpan_make_full_quoted_printable(const char * display_charset,
+ const char * phrase)
+{
+ int needs_quote;
+ char * str;
+
+ needs_quote = to_be_quoted(phrase, strlen(phrase), 0);
+ if (needs_quote) {
+ MMAPString * mmapstr;
+
+ mmapstr = mmap_string_new("");
+ quote_word(display_charset, mmapstr, phrase, strlen(phrase));
+ str = strdup(mmapstr->str);
+ mmap_string_free(mmapstr);
+ }
+ else {
+ str = strdup(phrase);
+ }
+
+ return str;
+}
+
+static char * etpan_make_quoted_printable(const char * display_charset,
+ const char * phrase, int subject)
+{
+ char * str;
+ const char * cur;
+ MMAPString * mmapstr;
+
+ mmapstr = mmap_string_new("");
+
+ cur = phrase;
+ while (* cur != '\0') {
+ const char * begin;
+ const char * end;
+ int do_quote;
+ int quote_words;
+
+ begin = cur;
+ end = begin;
+ quote_words = 0;
+ do_quote = 1;
+
+ while (* cur != '\0') {
+ get_word(cur, &cur, subject, &do_quote);
+ if (do_quote) {
+ quote_words = 1;
+ end = cur;
+ }
+ else
+ break;
+ if (* cur != '\0')
+ cur ++;
+ }
+
+ if (quote_words) {
+ quote_word(display_charset, mmapstr, begin, end - begin);
+
+ if ((* end == ' ') || (* end == '\t')) {
+ mmap_string_append_c(mmapstr, * end);
+ end ++;
+ }
+
+ if (* end != '\0') {
+ mmap_string_append_len(mmapstr, end, cur - end);
+ }
+ }
+ else {
+ mmap_string_append_len(mmapstr, begin, cur - begin);
+ }
+
+ if ((* cur == ' ') || (* cur == '\t')) {
+ mmap_string_append_c(mmapstr, * cur);
+ cur ++;
+ }
+ }
+
+ str = strdup(mmapstr->str);
+ mmap_string_free(mmapstr);
+
+ return str;
+}
+
+#pragma mark extract subject
+
+static inline int skip_subj_blob(char * subj, size_t * begin,
+ size_t length, int keep_bracket)
+{
+ if (keep_bracket)
+ return 0;
+
+ /* subj-blob = "[" *BLOBCHAR "]" *WSP */
+ size_t cur_token;
+
+ cur_token = * begin;
+
+ if (subj[cur_token] != '[')
+ return 0;
+
+ cur_token ++;
+
+ while (1) {
+ if (cur_token >= length)
+ return 0;
+
+ if (subj[cur_token] == '[')
+ return 0;
+
+ if (subj[cur_token] == ']')
+ break;
+
+ cur_token ++;
+ }
+
+ cur_token ++;
+
+ while (1) {
+ if (cur_token >= length)
+ break;
+
+ if (subj[cur_token] != ' ')
+ break;
+
+ cur_token ++;
+ }
+
+ * begin = cur_token;
+
+ return 1;
+}
+
+static inline int skip_subj_refwd(char * subj, size_t * begin,
+ size_t length, int keep_bracket)
+{
+ /* subj-refwd = ("re" / ("fw" ["d"])) *WSP [subj-blob] ":" */
+ size_t cur_token;
+ int prefix;
+ int has_suffix;
+
+ cur_token = * begin;
+ prefix = 0;
+ if (!prefix) {
+ if (length - cur_token >= 18) {
+ if (strncasecmp(subj + cur_token, "Переслать", 18) == 0) {
+ cur_token += 18;
+ prefix = 1;
+ }
+ }
+ }
+ if (!prefix) {
+ if (length - cur_token >= 10) {
+ if (strncasecmp(subj + cur_token, "Ответ", 10) == 0) {
+ cur_token += 10;
+ prefix = 1;
+ }
+ }
+ }
+ if (!prefix) {
+ if (length - cur_token >= 7) {
+ if (strncasecmp(subj + cur_token, "Antwort", 7) == 0) {
+ cur_token += 7;
+ prefix = 1;
+ }
+ }
+ }
+ if (!prefix) {
+ if (length - cur_token >= 6) {
+ if (strncasecmp(subj + cur_token, "回复", 6) == 0) {
+ cur_token += 6;
+ prefix = 1;
+ }
+ else if (strncasecmp(subj + cur_token, "转发", 6) == 0) {
+ cur_token += 6;
+ prefix = 1;
+ }
+ }
+ }
+ if (!prefix) {
+ if (length - cur_token >= 5) {
+ // é is 2 chars in utf-8
+ if (strncasecmp(subj + cur_token, "réf.", 5) == 0) {
+ cur_token += 5;
+ prefix = 1;
+ }
+ else if (strncasecmp(subj + cur_token, "rép.", 5) == 0) {
+ cur_token += 5;
+ prefix = 1;
+ }
+ else if (strncasecmp(subj + cur_token, "trans", 5) == 0) {
+ cur_token += 5;
+ prefix = 1;
+ }
+ }
+ }
+ if (!prefix) {
+ if (length - cur_token >= 4) {
+ if (strncasecmp(subj + cur_token, "antw", 4) == 0) {
+ cur_token += 4;
+ prefix = 1;
+ }
+ }
+ }
+ if (!prefix) {
+ if (length - cur_token >= 3) {
+ if (strncasecmp(subj + cur_token, "fwd", 3) == 0) {
+ cur_token += 3;
+ prefix = 1;
+ }
+ else if (strncasecmp(subj + cur_token, "ogg", 3) == 0) {
+ cur_token += 3;
+ prefix = 1;
+ }
+ else if (strncasecmp(subj + cur_token, "odp", 3) == 0) {
+ cur_token += 3;
+ prefix = 1;
+ }
+ else if (strncasecmp(subj + cur_token, "res", 3) == 0) {
+ cur_token += 3;
+ prefix = 1;
+ }
+ else if (strncasecmp(subj + cur_token, "end", 3) == 0) {
+ cur_token += 3;
+ prefix = 1;
+ }
+ }
+ }
+ if (!prefix) {
+ if (length - cur_token >= 2) {
+ if (strncasecmp(subj + cur_token, "fw", 2) == 0) {
+ cur_token += 2;
+ prefix = 1;
+ }
+ else if (strncasecmp(subj + cur_token, "re", 2) == 0) {
+ cur_token += 2;
+ prefix = 1;
+ }
+ else if (strncasecmp(subj + cur_token, "tr", 2) == 0) {
+ cur_token += 2;
+ prefix = 1;
+ }
+ else if (strncasecmp(subj + cur_token, "aw", 2) == 0) {
+ cur_token += 2;
+ prefix = 1;
+ }
+ else if (strncasecmp(subj + cur_token, "sv", 2) == 0) {
+ cur_token += 2;
+ prefix = 1;
+ }
+ else if (strncasecmp(subj + cur_token, "rv", 2) == 0) {
+ cur_token += 2;
+ prefix = 1;
+ }
+ }
+ }
+ if (!prefix) {
+ if (length - cur_token >= 1) {
+ if (strncasecmp(subj + cur_token, "r", 1) == 0) {
+ cur_token += 1;
+ prefix = 1;
+ }
+ }
+ }
+
+ if (!prefix)
+ return 0;
+
+ while (1) {
+ if (cur_token >= length)
+ break;
+
+ if (subj[cur_token] != ' ')
+ break;
+
+ cur_token ++;
+ }
+
+ skip_subj_blob(subj, &cur_token, length, keep_bracket);
+
+ has_suffix = 0;
+
+ if (!has_suffix) {
+ if (length - cur_token >= 3) {
+ if (strncasecmp(subj + cur_token, ":", 3) == 0) {
+ cur_token += 3;
+ has_suffix = 1;
+ }
+ }
+ }
+
+ if (!has_suffix) {
+ if (cur_token < length) {
+ if (subj[cur_token] == ':') {
+ cur_token ++;
+ has_suffix = 1;
+ }
+ }
+ }
+
+ if (!has_suffix) {
+ return 0;
+ }
+
+ * begin = cur_token;
+
+ return 1;
+}
+
+static inline int skip_subj_leader(char * subj, size_t * begin,
+ size_t length, int keep_bracket)
+{
+ size_t cur_token;
+
+ cur_token = * begin;
+
+ /* subj-leader = (*subj-blob subj-refwd) / WSP */
+
+ if (subj[cur_token] == ' ') {
+ cur_token ++;
+ }
+ else {
+ while (cur_token < length) {
+ if (!skip_subj_blob(subj, &cur_token, length, keep_bracket))
+ break;
+ }
+ if (!skip_subj_refwd(subj, &cur_token, length, keep_bracket))
+ return 0;
+ }
+
+ * begin = cur_token;
+
+ return 1;
+}
+
+static char * extract_subject(char * str, int keep_bracket)
+{
+ char * subj;
+ char * cur;
+ char * write_pos;
+ size_t len;
+ size_t begin;
+ int do_repeat_5;
+ int do_repeat_6;
+
+ /*
+ (1) Convert any RFC 2047 encoded-words in the subject to
+ UTF-8.
+ We work on UTF-8 string -- DVH
+ */
+
+ subj = strdup(str);
+ if (subj == NULL)
+ return NULL;
+
+ len = strlen(subj);
+
+ /*
+ Convert all tabs and continuations to space.
+ Convert all multiple spaces to a single space.
+ */
+
+ cur = subj;
+ write_pos = subj;
+ while (* cur != '\0') {
+ int cont;
+
+ switch (* cur) {
+ case '\t':
+ case '\r':
+ case '\n':
+ cont = 1;
+
+ cur ++;
+ while (* cur && cont) {
+ switch (* cur) {
+ case '\t':
+ case '\r':
+ case '\n':
+ cont = 1;
+ break;
+ default:
+ cont = 0;
+ break;
+ }
+ cur ++;
+ }
+
+ * write_pos = ' ';
+ write_pos ++;
+
+ break;
+
+ default:
+ * write_pos = * cur;
+ write_pos ++;
+
+ cur ++;
+
+ break;
+ }
+ }
+ * write_pos = '\0';
+
+ begin = 0;
+
+ do {
+ do_repeat_6 = 0;
+
+ /*
+ (2) Remove all trailing text of the subject that matches
+ the subj-trailer ABNF, repeat until no more matches are
+ possible.
+ */
+
+ while (len > 0) {
+ int chg;
+
+ chg = 0;
+
+ /* subj-trailer = "(fwd)" / WSP */
+ if (subj[len - 1] == ' ') {
+ subj[len - 1] = '\0';
+ len --;
+ }
+ else {
+ if (len < 5)
+ break;
+
+ if (strncasecmp(subj + len - 5, "(fwd)", 5) != 0)
+ break;
+
+ subj[len - 5] = '\0';
+ len -= 5;
+ }
+ }
+
+ do {
+ size_t saved_begin;
+
+ do_repeat_5 = 0;
+
+ /*
+ (3) Remove all prefix text of the subject that matches the
+ subj-leader ABNF.
+ */
+
+ if (skip_subj_leader(subj, &begin, len, keep_bracket))
+ do_repeat_5 = 1;
+
+ /*
+ (4) If there is prefix text of the subject that matches the
+ subj-blob ABNF, and removing that prefix leaves a non-empty
+ subj-base, then remove the prefix text.
+ */
+
+ saved_begin = begin;
+ if (skip_subj_blob(subj, &begin, len, keep_bracket)) {
+ if (begin == len) {
+ /* this will leave a empty subject base */
+ begin = saved_begin;
+ }
+ else
+ do_repeat_5 = 1;
+ }
+
+ /*
+ (5) Repeat (3) and (4) until no matches remain.
+ Note: it is possible to defer step (2) until step (6),
+ but this requires checking for subj-trailer in step (4).
+ */
+
+ }
+ while (do_repeat_5);
+
+ /*
+ (6) If the resulting text begins with the subj-fwd-hdr ABNF
+ and ends with the subj-fwd-trl ABNF, remove the
+ subj-fwd-hdr and subj-fwd-trl and repeat from step (2).
+ */
+
+ if (len >= 5) {
+ size_t saved_begin;
+
+ saved_begin = begin;
+ if (strncasecmp(subj + begin, "[fwd:", 5) == 0) {
+ begin += 5;
+
+ if (subj[len - 1] != ']')
+ saved_begin = begin;
+ else {
+ subj[len - 1] = '\0';
+ len --;
+ do_repeat_6 = 1;
+ }
+ }
+ }
+
+ }
+ while (do_repeat_6);
+
+ /*
+ (7) The resulting text is the "base subject" used in
+ threading.
+ */
+
+ /* convert to upper case */
+
+ cur = subj + begin;
+ write_pos = subj;
+
+ while (* cur != '\0') {
+ * write_pos = * cur;
+ cur ++;
+ write_pos ++;
+ }
+ * write_pos = '\0';
+
+ return subj;
+}
+
+String::String(const UChar * unicodeChars)
+{
+ mUnicodeChars = NULL;
+ reset();
+ appendCharacters(unicodeChars);
+}
+
+String::String(const UChar * unicodeChars, unsigned int length)
+{
+ mUnicodeChars = NULL;
+ reset();
+ appendCharactersLength(unicodeChars, length);
+}
+
+String::String(const char * UTF8Characters)
+{
+ mUnicodeChars = NULL;
+ reset();
+ appendUTF8Characters(UTF8Characters);
+}
+
+String::String(String * otherString)
+{
+ mUnicodeChars = NULL;
+ reset();
+ appendString(otherString);
+}
+
+String::String(Data * data, const char * charset)
+{
+ mUnicodeChars = NULL;
+ reset();
+ appendBytes(data->bytes(), data->length(), charset);
+}
+
+String::String(const char * bytes, unsigned int length, const char * charset)
+{
+ mUnicodeChars = NULL;
+ reset();
+ if (charset == NULL) {
+ appendUTF8CharactersLength(bytes, length);
+ }
+ else {
+ appendBytes(bytes, length, charset);
+ }
+}
+
+String::~String()
+{
+ reset();
+}
+
+void String::allocate(unsigned int length)
+{
+ length ++;
+ if (length < mAllocated)
+ return;
+
+ if (mAllocated == 0) {
+ mAllocated = 4;
+ }
+ while (length > mAllocated) {
+ mAllocated *= 2;
+ }
+
+ mUnicodeChars = (UChar *) realloc(mUnicodeChars, mAllocated * sizeof(* mUnicodeChars));
+}
+
+String * String::string()
+{
+ return stringWithCharacters(NULL);
+}
+
+String * String::stringWithUTF8Format(const char * format, ...)
+{
+ va_list argp;
+
+ va_start(argp, format);
+ String * result = stringWithVUTF8Format(format, argp);
+ va_end(argp);
+
+ return result;
+}
+
+String * String::stringWithVUTF8Format(const char * format, va_list ap)
+{
+ char * result;
+ vasprintf(&result, format, ap);
+ return stringWithUTF8Characters(result);
+}
+
+String * String::stringWithUTF8Characters(const char * UTF8Characters)
+{
+ String * result = new String(UTF8Characters);
+ return (String *) result->autorelease();
+}
+
+String * String::stringWithCharacters(const UChar * characters)
+{
+ String * result = new String(characters);
+ return (String *) result->autorelease();
+}
+
+String * String::stringWithCharacters(const UChar * characters, unsigned int length)
+{
+ String * result = new String(characters, length);
+ return (String *) result->autorelease();
+}
+
+void String::appendCharactersLength(const UChar * unicodeCharacters, unsigned int length)
+{
+ allocate(mLength + length);
+ u_strncpy(&mUnicodeChars[mLength], unicodeCharacters, length);
+ mLength += length;
+ mUnicodeChars[mLength] = 0;
+}
+
+void String::appendString(String * otherString)
+{
+ appendCharactersLength(otherString->unicodeCharacters(), otherString->length());
+}
+
+void String::appendUTF8Format(const char * format, ...)
+{
+ va_list argp;
+
+ va_start(argp, format);
+ String * otherString = stringWithVUTF8Format(format, argp);
+ va_end(argp);
+
+ this->appendString(otherString);
+}
+
+void String::appendUTF8CharactersLength(const char * UTF8Characters, unsigned int length)
+{
+ if (UTF8Characters == NULL)
+ return;
+
+ UChar * dest;
+ int32_t destLength;
+ int32_t destCapacity;
+ UErrorCode err;
+
+ err = U_ZERO_ERROR;
+ u_strFromUTF8WithSub(NULL, 0, &destLength, UTF8Characters, length, 0xFFFD, NULL, &err);
+ destCapacity = destLength + 1;
+ dest = (UChar *) malloc(destCapacity * sizeof(* dest));
+ err = U_ZERO_ERROR;
+ u_strFromUTF8WithSub(dest, destCapacity, &destLength, UTF8Characters, length, 0xFFFD, NULL, &err);
+ dest[destLength] = 0;
+ // Fix in case of bad conversion.
+ for(int32_t i = 0 ; i < destLength ; i ++) {
+ if (dest[i] == 0) {
+ dest[i] = ' ';
+ }
+ }
+
+ appendCharactersLength(dest, destLength);
+
+ free(dest);
+}
+
+void String::appendUTF8Characters(const char * UTF8Characters)
+{
+ appendUTF8CharactersLength(UTF8Characters, (unsigned int) strlen(UTF8Characters));
+}
+
+void String::appendCharacters(const UChar * unicodeCharacters)
+{
+ if (unicodeCharacters == NULL)
+ return;
+
+ appendCharactersLength(unicodeCharacters, u_strlen(unicodeCharacters));
+}
+
+const UChar * String::unicodeCharacters()
+{
+ return mUnicodeChars;
+}
+
+const char * String::UTF8Characters()
+{
+ char * dest;
+ int32_t destLength;
+ int32_t destCapacity;
+ UErrorCode err;
+
+ err = U_ZERO_ERROR;
+ u_strToUTF8(NULL, 0, &destLength, mUnicodeChars, mLength, &err);
+ destCapacity = destLength + 1;
+ dest = (char *) malloc(destCapacity * sizeof(* dest));
+ err = U_ZERO_ERROR;
+ u_strToUTF8(dest, destCapacity, &destLength, mUnicodeChars, mLength, &err);
+ dest[destLength] = 0;
+
+ Data * data = Data::dataWithBytes(dest, destLength + 1);
+ free(dest);
+
+ return data->bytes();
+}
+
+unsigned int String::length()
+{
+ return mLength;
+}
+
+String * String::stringByAppendingString(String * otherString)
+{
+ String * result = new String(this);
+ result->appendString(otherString);
+ return (String *) result->autorelease();
+}
+
+String * String::stringByAppendingUTF8Format(const char * format, ...)
+{
+ va_list argp;
+
+ va_start(argp, format);
+ String * otherString = stringWithVUTF8Format(format, argp);
+ va_end(argp);
+
+ return this->stringByAppendingString(otherString);
+}
+
+String * String::stringByAppendingUTF8Characters(const char * UTF8Characters)
+{
+ String * otherString = stringWithUTF8Characters(UTF8Characters);
+ return this->stringByAppendingString(otherString);
+}
+
+String * String::stringByAppendingCharacters(const UChar * unicodeCharacters)
+{
+ String * otherString = stringWithCharacters(unicodeCharacters);
+ return this->stringByAppendingString(otherString);
+}
+
+void String::reset()
+{
+ free(mUnicodeChars);
+ mUnicodeChars = NULL;
+ mLength = 0;
+ mAllocated = 0;
+}
+
+void String::setString(String * otherString)
+{
+ reset();
+ appendString(otherString);
+}
+
+void String::setUTF8Characters(const char * UTF8Characters)
+{
+ reset();
+ appendUTF8Characters(UTF8Characters);
+}
+
+void String::setCharacters(const UChar * unicodeCharacters)
+{
+ reset();
+ appendCharacters(unicodeCharacters);
+}
+
+#if 0
+String * String::className()
+{
+ return MCSTR("String");
+}
+#endif
+
+String * String::description()
+{
+ return this;
+}
+
+Object * String::copy()
+{
+ return new String(this);
+}
+
+bool String::isEqual(Object * otherObject)
+{
+ String * otherString = (String *) otherObject;
+ if (length() != otherString->length())
+ return false;
+ return compare(otherString) == 0;
+}
+
+unsigned int String::hash()
+{
+ return hashCompute((const char *) mUnicodeChars, mLength * sizeof(* mUnicodeChars));
+}
+
+#define DEFAULT_INCOMING_CHARSET "iso-8859-1"
+#define DEFAULT_DISPLAY_CHARSET "utf-8"
+
+String * String::stringByDecodingMIMEHeaderValue(const char * phrase)
+{
+ size_t cur_token;
+ char * decoded;
+ String * result;
+ bool hasEncoding;
+
+ if (phrase == NULL)
+ return string();
+
+ if (* phrase == '\0') {
+ return string();
+ }
+
+ hasEncoding = false;
+ if (strstr(phrase, "=?") != NULL) {
+ if ((strcasestr(phrase, "?Q?") != NULL) || (strcasestr(phrase, "?B?") != NULL)) {
+ hasEncoding = true;
+ }
+ }
+
+ if (!hasEncoding) {
+ return Data::dataWithBytes(phrase, (unsigned int) strlen(phrase))->stringWithDetectedCharset();
+ }
+
+ cur_token = 0;
+ decoded = NULL;
+ mailmime_encoded_phrase_parse(DEFAULT_INCOMING_CHARSET,
+ phrase, strlen(phrase),
+ &cur_token, DEFAULT_DISPLAY_CHARSET,
+ &decoded);
+
+ result = NULL;
+ if (decoded != NULL) {
+ result = stringWithUTF8Characters(decoded);
+ }
+ else {
+ MCLog("could not decode: %s\n", phrase);
+ }
+
+ free(decoded);
+
+ return result;
+}
+
+Data * String::encodedAddressDisplayNameValue()
+{
+ char * str;
+ Data * result;
+
+ str = etpan_make_full_quoted_printable(DEFAULT_DISPLAY_CHARSET, UTF8Characters());
+ result = Data::dataWithBytes(str, (unsigned int) strlen(str) + 1);
+ free(str);
+
+ return result;
+}
+
+Data * String::encodedMIMEHeaderValue()
+{
+ char * str;
+ Data * result;
+
+ str = etpan_make_quoted_printable(DEFAULT_DISPLAY_CHARSET, UTF8Characters(), 0);
+ result = Data::dataWithBytes(str, (unsigned int) strlen(str) + 1);
+ free(str);
+
+ return result;
+}
+
+Data * String::encodedMIMEHeaderValueForSubject()
+{
+ char * str;
+ Data * result;
+
+ str = etpan_make_quoted_printable(DEFAULT_DISPLAY_CHARSET, UTF8Characters(), 1);
+ result = Data::dataWithBytes(str, (unsigned int) strlen(str) + 1);
+ free(str);
+
+ return result;
+}
+
+int String::compareWithCaseSensitive(String * otherString, bool caseSensitive)
+{
+ if ((unicodeCharacters() == NULL) && (otherString->unicodeCharacters() != NULL)) {
+ return 0;
+ }
+
+ if (unicodeCharacters() == NULL) {
+ return -1;
+ }
+
+ if (otherString->unicodeCharacters() == NULL) {
+ return -1;
+ }
+
+ if (caseSensitive) {
+ return u_strcmp(unicodeCharacters(), otherString->unicodeCharacters());
+ }
+ else {
+ return u_strcasecmp(unicodeCharacters(), otherString->unicodeCharacters(), 0);
+ }
+}
+
+int String::compare(String * otherString)
+{
+ return compareWithCaseSensitive(otherString, true);
+}
+
+int String::caseInsensitiveCompare(String * otherString)
+{
+ return compareWithCaseSensitive(otherString, false);
+}
+
+String * String::lowercaseString()
+{
+ UErrorCode err;
+ String * result = (String *) copy()->autorelease();
+ err = U_ZERO_ERROR;
+ u_strToLower(result->mUnicodeChars, result->mLength,
+ result->mUnicodeChars, result->mLength,
+ NULL, &err);
+ return result;
+}
+
+String * String::uppercaseString()
+{
+ UErrorCode err;
+ String * result = (String *) copy()->autorelease();
+ err = U_ZERO_ERROR;
+ u_strToUpper(result->mUnicodeChars, result->mLength,
+ result->mUnicodeChars, result->mLength,
+ NULL, &err);
+ return result;
+}
+
+void String::appendBytes(const char * bytes, unsigned int length, const char * charset)
+{
+ UErrorCode err;
+
+ err = U_ZERO_ERROR;
+ UConverter * converter = ucnv_open(charset, &err);
+ if (converter == NULL) {
+ MCLog("invalid charset %s %i", charset, err);
+ return;
+ }
+
+ err = U_ZERO_ERROR;
+ int32_t destLength = ucnv_toUChars(converter, NULL, 0,
+ bytes, length, &err);
+ int32_t destCapacity = destLength + 1;
+ UChar * dest = (UChar *) malloc(destCapacity * sizeof(* dest));
+ err = U_ZERO_ERROR;
+ destLength = ucnv_toUChars(converter, dest, destCapacity, bytes, length, &err);
+ dest[destLength] = 0;
+
+ // Fix in case of bad conversion.
+ for(int32_t i = 0 ; i < destLength ; i ++) {
+ if (dest[i] == 0) {
+ dest[i] = ' ';
+ }
+ }
+
+ appendCharactersLength(dest, destLength);
+ free(dest);
+
+ ucnv_close(converter);
+}
+
+String * String::extractedSubject()
+{
+ return extractedSubjectAndKeepBracket(false);
+}
+
+String * String::extractedSubjectAndKeepBracket(bool keepBracket)
+{
+ char * result;
+ String * str;
+
+ result = extract_subject((char *) UTF8Characters(), keepBracket);
+ str = String::stringWithUTF8Characters(result);
+ free(result);
+
+ return str;
+}
+
+String * String::uuidString()
+{
+ uuid_t uuid;
+ uuid_string_t uuidString;
+
+ uuid_generate(uuid);
+ uuid_unparse_lower(uuid, uuidString);
+ return String::stringWithUTF8Characters(uuidString);
+}
+
+unsigned int String::replaceOccurrencesOfString(String * occurrence, String * replacement)
+{
+ unsigned int count;
+
+ if (occurrence->length() == 0)
+ return 0;
+
+ count = 0;
+ UChar * p = mUnicodeChars;
+ while (1) {
+ UChar * location;
+ location = u_strstr(p, occurrence->unicodeCharacters());
+ if (location == NULL)
+ break;
+ p = location + 1;
+ count ++;
+ }
+
+ UChar * unicodeChars;
+ int delta = replacement->length() - occurrence->length();
+ int modifiedLength = mLength + delta * count + 1;
+ unicodeChars = (UChar *) malloc(modifiedLength * sizeof(* unicodeChars));
+ UChar * dest_p = unicodeChars;
+ p = mUnicodeChars;
+ while (1) {
+ UChar * location;
+ unsigned int count;
+
+ location = u_strstr(p, occurrence->unicodeCharacters());
+ if (location == NULL)
+ break;
+ count = (unsigned int) (location - p);
+ u_memcpy(dest_p, p, count);
+ dest_p += count;
+ p += count;
+ u_memcpy(dest_p, p, replacement->length());
+ p += occurrence->length();
+ dest_p += replacement->length();
+ }
+ // copy remaining
+ u_strcpy(dest_p, p);
+
+ return count;
+}
+
+UChar String::characterAtIndex(unsigned int index)
+{
+ return mUnicodeChars[index];
+}
+
+void String::deleteCharactersInRange(Range range)
+{
+ if (range.index > mLength)
+ return;
+
+ if (range.index + range.length > mLength) {
+ range.length = mLength - range.index;
+ }
+
+ int32_t count = mLength - (range.index + range.length);
+ u_memmove(&mUnicodeChars[range.index], &mUnicodeChars[range.index + range.length], count);
+}
+
+int String::locationOfString(String * occurrence)
+{
+ UChar * location;
+ location = u_strstr(mUnicodeChars, occurrence->unicodeCharacters());
+ if (location == NULL) {
+ return -1;
+ }
+
+ return (int) (location - mUnicodeChars);
+}
+
+#pragma mark strip HTML
+
+struct parserState {
+ int level;
+ int enabled;
+ int disabledLevel;
+ String * result;
+ int logEnabled;
+ int hasQuote;
+ int quoteLevel;
+ bool hasText;
+ bool lastCharIsWhitespace;
+ bool showBlockQuote;
+ bool showLink;
+ bool hasReturnToLine;
+ Array * linkStack;
+ Array * paragraphSpacingStack;
+};
+
+static void appendQuote(struct parserState * state);
+
+static void charactersParsed(void * context,
+ const xmlChar * ch, int len)
+/*" Callback function for stringByStrippingHTML. "*/
+{
+ struct parserState * state;
+
+ state = (struct parserState *) context;
+ String * result = state->result;
+
+ if (!state->enabled) {
+ return;
+ }
+
+ if (state->logEnabled) {
+ MCLog("text %s", ch);
+ }
+ String * modifiedString;
+ modifiedString = new String((const char *) ch, len);
+ //modifiedString->replaceOccurrencesOfString(MCSTR("\r\n"), MCSTR(" "));
+ modifiedString->replaceOccurrencesOfString(MCSTR("\n"), MCSTR(" "));
+ modifiedString->replaceOccurrencesOfString(MCSTR("\r"), MCSTR(" "));
+ modifiedString->replaceOccurrencesOfString(MCSTR("\t"), MCSTR(" "));
+
+ UChar specialCh[2];
+ specialCh[0] = 133;
+ specialCh[1] = 0;
+ modifiedString->replaceOccurrencesOfString(String::stringWithCharacters(specialCh), MCSTR(" "));
+
+ while (modifiedString->replaceOccurrencesOfString(MCSTR(" "), MCSTR(" ")) > 0) {
+ }
+
+ if (modifiedString->length() > 0) {
+ if (state->lastCharIsWhitespace) {
+ if (modifiedString->characterAtIndex(0) == ' ') {
+ modifiedString->deleteCharactersInRange(RangeMake(0, 1));
+ }
+ }
+ }
+
+ if (modifiedString->length() > 0) {
+ bool lastIsWhiteSpace;
+ bool isWhiteSpace;
+
+ isWhiteSpace = false;
+ lastIsWhiteSpace = false;
+ if (modifiedString->length() > 0) {
+ if (modifiedString->characterAtIndex(modifiedString->length() - 1) == ' ') {
+ lastIsWhiteSpace = true;
+ }
+ }
+ if (lastIsWhiteSpace && (modifiedString->length() == 1)) {
+ isWhiteSpace = true;
+ }
+
+ if (isWhiteSpace) {
+ if (state->lastCharIsWhitespace) {
+ // do nothing
+ }
+ else if (!state->hasText) {
+ // do nothing
+ }
+ else {
+ result->appendString(MCSTR(" "));
+ state->lastCharIsWhitespace = true;
+ state->hasText = true;
+ }
+ }
+ else {
+ if (!state->hasQuote) {
+ appendQuote(state);
+ state->hasQuote = true;
+ }
+ result->appendString(modifiedString);
+ state->lastCharIsWhitespace = lastIsWhiteSpace;
+ state->hasText = true;
+ }
+ }
+ modifiedString->release();
+}
+
+/* GCS: custom error function to ignore errors */
+static void structuredError(void * userData,
+ xmlErrorPtr error)
+{
+ /* ignore all errors */
+ (void)userData;
+ (void)error;
+}
+
+static void appendQuote(struct parserState * state)
+{
+ if (state->quoteLevel < 0) {
+ MCLog("error consistency in quote level");
+ state->lastCharIsWhitespace = true;
+ return;
+ }
+ for(int i = 0 ; i < state->quoteLevel ; i ++) {
+ state->result->appendString(MCSTR("> "));
+ }
+ state->lastCharIsWhitespace = true;
+}
+
+static void returnToLine(struct parserState * state)
+{
+ if (!state->hasQuote) {
+ appendQuote(state);
+ state->hasQuote = true;
+ }
+ state->result->appendString(MCSTR("\n"));
+ state->hasText = false;
+ state->lastCharIsWhitespace = true;
+ state->hasQuote = false;
+ state->hasReturnToLine = false;
+}
+
+static void returnToLineAtBeginningOfBlock(struct parserState * state)
+{
+ if (state->hasText) {
+ returnToLine(state);
+ }
+ state->hasQuote = false;
+}
+
+static Set * blockElements(void)
+{
+ static Set * elements = NULL;
+ pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+ pthread_mutex_lock(&lock);
+ if (elements == NULL) {
+ elements = new Set();
+ elements->addObject(MCSTR("address"));
+ elements->addObject(MCSTR("div"));
+ elements->addObject(MCSTR("p"));
+ elements->addObject(MCSTR("h1"));
+ elements->addObject(MCSTR("h2"));
+ elements->addObject(MCSTR("h3"));
+ elements->addObject(MCSTR("h4"));
+ elements->addObject(MCSTR("h5"));
+ elements->addObject(MCSTR("h6"));
+ elements->addObject(MCSTR("pre"));
+ elements->addObject(MCSTR("ul"));
+ elements->addObject(MCSTR("ol"));
+ elements->addObject(MCSTR("li"));
+ elements->addObject(MCSTR("dl"));
+ elements->addObject(MCSTR("dt"));
+ elements->addObject(MCSTR("dd"));
+ elements->addObject(MCSTR("form"));
+ // tables
+ elements->addObject(MCSTR("col"));
+ elements->addObject(MCSTR("colgroup"));
+ elements->addObject(MCSTR("th"));
+ elements->addObject(MCSTR("tbody"));
+ elements->addObject(MCSTR("thead"));
+ elements->addObject(MCSTR("tfoot"));
+ elements->addObject(MCSTR("table"));
+ elements->addObject(MCSTR("tr"));
+ elements->addObject(MCSTR("td"));
+ }
+ pthread_mutex_unlock(&lock);
+
+ return elements;
+}
+
+static HashMap * dictionaryFromAttributes(const xmlChar ** atts)
+{
+ HashMap * result;
+
+ if (atts == NULL)
+ return NULL;
+
+ result = HashMap::hashMap();
+ for(const xmlChar ** curAtt = atts ; * curAtt != NULL ; curAtt += 2) {
+ const xmlChar * attrName;
+ const xmlChar * attrValue;
+ String * name;
+
+ attrName = * curAtt;
+ attrValue = * (curAtt + 1);
+ if ((attrName == NULL) || (attrValue == NULL))
+ continue;
+
+ name = String::stringWithUTF8Characters((const char *) attrName);
+ name = name->lowercaseString();
+ result->setObjectForKey(name, String::stringWithUTF8Characters((const char *) attrValue));
+ }
+
+ return result;
+}
+
+static void elementStarted(void * ctx, const xmlChar * name, const xmlChar ** atts)
+{
+ struct parserState * state;
+
+ state = (struct parserState *) ctx;
+
+ if (state->logEnabled) {
+ MCLog("parsed element %s", name);
+ }
+
+ if (strcasecmp((const char *) name, "blockquote") == 0) {
+ state->quoteLevel ++;
+ }
+ else if (strcasecmp((const char *) name, "a") == 0) {
+ AutoreleasePool * pool;
+ String * link;
+ HashMap * attributes;
+
+ pool = new AutoreleasePool();
+ attributes = dictionaryFromAttributes(atts);
+ link = (String *) attributes->objectForKey(MCSTR("href"));
+ if (link == NULL)
+ link = MCSTR("");
+
+ Array * item;
+ item = new Array();
+ item->addObject(link);
+ item->addObject(Value::valueWithUnsignedIntValue(state->result->length()));
+ state->linkStack->addObject(item);
+ item->release();
+ pool->release();
+ }
+ else if (strcasecmp((const char *) name, "p") == 0) {
+ bool hasSpacing;
+ String * style;
+ AutoreleasePool * pool;
+ HashMap * attributes;
+
+ hasSpacing = true;
+
+ pool = new AutoreleasePool();
+ attributes = dictionaryFromAttributes(atts);
+ style = (String *) attributes->objectForKey(MCSTR("style"));
+ if (style->locationOfString(MCSTR("margin: 0.0px 0.0px 0.0px 0.0px;")) != -1) {
+ hasSpacing = false;
+ }
+ else if (style->locationOfString(MCSTR("margin: 0px 0px 0px 0px;")) != -1) {
+ hasSpacing = false;
+ }
+ else if (style->locationOfString(MCSTR("margin: 0.0px;")) != -1) {
+ hasSpacing = false;
+ }
+ else if (style->locationOfString(MCSTR("margin: 0px;")) != -1) {
+ hasSpacing = false;
+ }
+ pool->release();
+
+ state->paragraphSpacingStack->addObject(Value::valueWithBoolValue(hasSpacing));
+ }
+
+ if (state->enabled) {
+ if (state->level == 1) {
+ if (strcasecmp((const char *) name, "head") == 0) {
+ state->enabled = 0;
+ state->disabledLevel = state->level;
+ }
+ }
+ if (strcasecmp((const char *) name, "style") == 0) {
+ state->enabled = 0;
+ state->disabledLevel = state->level;
+ }
+ else if (strcasecmp((const char *) name, "script") == 0) {
+ state->enabled = 0;
+ state->disabledLevel = state->level;
+ }
+ else if (strcasecmp((const char *) name, "p") == 0) {
+ returnToLineAtBeginningOfBlock(state);
+ if (((Value *) state->paragraphSpacingStack->lastObject())->boolValue()) {
+ returnToLine(state);
+ }
+ }
+ else if (blockElements()->containsObject(String::stringWithUTF8Characters((const char *) name)->lowercaseString())) {
+ returnToLineAtBeginningOfBlock(state);
+ }
+ else if (strcasecmp((const char *) name, "blockquote") == 0) {
+ if (!state->showBlockQuote) {
+ AutoreleasePool * pool;
+ String * type;
+ bool cite;
+ HashMap * attributes;
+
+ cite = false;
+ pool = new AutoreleasePool();
+ attributes = dictionaryFromAttributes(atts);
+ type = (String *) attributes->objectForKey(MCSTR("type"));
+ if (type->caseInsensitiveCompare(MCSTR("cite")) == 0) {
+ cite = true;
+ }
+ pool->release();
+
+ if (cite) {
+ state->enabled = 0;
+ state->disabledLevel = state->level;
+ }
+ else {
+ returnToLineAtBeginningOfBlock(state);
+ }
+ }
+ else {
+ returnToLineAtBeginningOfBlock(state);
+ }
+ }
+ else if (strcasecmp((const char *) name, "br") == 0) {
+ returnToLine(state);
+ state->hasReturnToLine = true;
+ }
+ }
+
+ state->level ++;
+}
+
+static void elementEnded(void * ctx, const xmlChar * name)
+{
+ struct parserState * state;
+
+ state = (struct parserState *) ctx;
+
+ if (state->logEnabled) {
+ MCLog("ended element %s", name);
+ }
+
+ if (strcasecmp((const char *) name, "blockquote") == 0) {
+ state->quoteLevel --;
+ }
+
+ state->level --;
+ if (!state->enabled) {
+ if (state->level == state->disabledLevel) {
+ state->enabled = 1;
+ }
+ }
+
+ bool hasReturnToLine;
+
+ hasReturnToLine = false;
+ if (strcasecmp((const char *) name, "a") == 0) {
+ if (state->enabled) {
+ Array * item;
+ String * link;
+ unsigned int offset;
+
+ item = (Array *) state->linkStack->lastObject();
+ link = (String *) item->objectAtIndex(0);
+ offset = ((Value *) item->objectAtIndex(1))->unsignedIntValue();
+ if (state->showLink) {
+ if (offset != state->result->length()) {
+ if (link->length() > 0) {
+ if (!state->result->hasSuffix(link)) {
+ state->result->appendUTF8Characters("(");
+ state->result->appendString(link);
+ state->result->appendUTF8Characters(")");
+ state->hasText = true;
+ state->lastCharIsWhitespace = false;
+ }
+ }
+ }
+ }
+ }
+
+ state->linkStack->removeObjectAtIndex(state->linkStack->count() - 1);
+ }
+ else if (strcasecmp((const char *) name, "p") == 0) {
+ if (state->enabled) {
+ if (((Value *) state->paragraphSpacingStack->lastObject())->boolValue()) {
+ returnToLine(state);
+ }
+ }
+ state->paragraphSpacingStack->removeObjectAtIndex(state->paragraphSpacingStack->count() - 1);
+ hasReturnToLine = true;
+ }
+ else if (blockElements()->containsObject(String::stringWithUTF8Characters((const char *) name)->lowercaseString())) {
+ hasReturnToLine = true;
+ }
+ else if (strcasecmp((const char *) name, "blockquote") == 0) {
+ hasReturnToLine = true;
+ }
+
+ if (hasReturnToLine) {
+ if (state->enabled) {
+ if (!state->hasReturnToLine) {
+ returnToLine(state);
+ }
+ }
+ }
+}
+
+static void commentParsed(void * ctx, const xmlChar * value)
+{
+ struct parserState * state;
+
+ state = (struct parserState *) ctx;
+
+ if (state->logEnabled) {
+ MCLog("comments %s", value);
+ }
+}
+
+void initializeLibXML()
+{
+ static bool initDone = false;
+ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+ pthread_mutex_lock(&lock);
+ if (!initDone) {
+ initDone = true;
+ xmlInitParser();
+
+ /* GCS: override structuredErrorFunc to mine so
+ I can ignore errors */
+ xmlSetStructuredErrorFunc(xmlGenericErrorContext,
+ &structuredError);
+ }
+ pthread_mutex_unlock(&lock);
+}
+
+String * String::flattenHTMLAndShowBlockquoteAndLink(bool showBlockquote, bool showLink)
+/*" Interpretes the receiver als HTML, removes all tags
+ and returns the plain text. "*/
+{
+ initializeLibXML();
+
+ int mem_base = xmlMemBlocks();
+ String * result = String::string();
+ xmlSAXHandler handler;
+ bzero(&handler, sizeof(xmlSAXHandler));
+ handler.characters = &charactersParsed;
+ handler.startElement = elementStarted;
+ handler.endElement = elementEnded;
+ handler.comment = commentParsed;
+ struct parserState state;
+ state.result = result;
+ state.level = 0;
+ state.enabled = 1;
+ state.logEnabled = 0;
+ state.disabledLevel = 0;
+ state.quoteLevel = 0;
+ state.hasText = false;
+ state.hasQuote = false;
+ state.hasReturnToLine = false;
+ state.showBlockQuote = showBlockquote;
+ state.showLink = showLink;
+ state.lastCharIsWhitespace = true;
+ state.linkStack = new Array();
+ state.paragraphSpacingStack = new Array();
+
+ htmlSAXParseDoc((xmlChar*) UTF8Characters(), "utf-8", &handler, &state);
+
+ if (mem_base != xmlMemBlocks()) {
+ MCLog("Leak of %d blocks found in htmlSAXParseDoc",
+ xmlMemBlocks() - mem_base);
+ }
+
+ state.paragraphSpacingStack->release();
+ state.linkStack->release();
+
+ UChar ch[2];
+ ch[0] = 160;
+ ch[1] = 0;
+ result->replaceOccurrencesOfString(String::stringWithCharacters(ch), MCSTR(" "));
+
+ return result;
+}
+
+String * String::flattenHTMLAndShowBlockquote(bool showBlockquote)
+{
+ return flattenHTMLAndShowBlockquoteAndLink(showBlockquote, true);
+}
+
+String * String::flattenHTML()
+{
+ return flattenHTMLAndShowBlockquote(true);
+}
+
+bool String::hasSuffix(String * suffix)
+{
+ if (mLength > suffix->mLength) {
+ if (u_memcmp(suffix->mUnicodeChars + (mLength - suffix->mLength),
+ mUnicodeChars, suffix->mLength) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool String::hasPrefix(String * prefix)
+{
+ if (mLength > prefix->mLength) {
+ if (u_memcmp(prefix->mUnicodeChars, mUnicodeChars, prefix->mLength) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+String * String::lastPathComponent()
+{
+ UChar * component = u_strrchr(mUnicodeChars, '/');
+ if (component == NULL)
+ return (String *) this->copy()->autorelease();
+ return String::stringWithCharacters(component + 1);
+}
+
+String * String::pathExtension()
+{
+ UChar * component = u_strrchr(mUnicodeChars, '.');
+ if (component == NULL)
+ return MCSTR("");
+ return String::stringWithCharacters(component + 1);
+}
+
+Data * String::dataUsingEncoding(const char * charset)
+{
+ UErrorCode err;
+ Data * data;
+
+ if (charset == NULL) {
+ charset = "utf-8";
+ }
+
+ err = U_ZERO_ERROR;
+ UConverter * converter = ucnv_open(charset, &err);
+ if (converter == NULL) {
+ MCLog("invalid charset %s %i", charset, err);
+ return NULL;
+ }
+
+ err = U_ZERO_ERROR;
+ int32_t destLength = ucnv_fromUChars(converter, NULL, 0, mUnicodeChars, mLength, &err);
+ int32_t destCapacity = destLength + 1;
+ char * dest = (char *) malloc(destCapacity * sizeof(* dest));
+ err = U_ZERO_ERROR;
+ destLength = ucnv_fromUChars(converter, dest, destCapacity, mUnicodeChars, mLength, &err);
+ dest[destLength] = 0;
+
+ // Fix in case of bad conversion.
+ for(int32_t i = 0 ; i < destLength ; i ++) {
+ if (dest[i] == 0) {
+ dest[i] = ' ';
+ }
+ }
+
+ data = Data::dataWithBytes(dest, destLength);
+
+ free(dest);
+
+ ucnv_close(converter);
+
+ return data;
+}
+
+const char * String::fileSystemRepresentation()
+{
+ return UTF8Characters();
+}
+
+String * String::stringWithFileSystemRepresentation(const char * filename)
+{
+ return stringWithUTF8Characters(filename);
+}
+
+Array * String::componentsSeparatedByString(String * separator)
+{
+ UChar * p;
+ Array * result;
+
+ result = Array::array();
+ p = mUnicodeChars;
+ while (1) {
+ UChar * location;
+ location = u_strstr(p, separator->unicodeCharacters());
+ if (location == NULL) {
+ break;
+ }
+
+ unsigned int length = (unsigned int) (p - location);
+ String * value = new String(p, length);
+ result->addObject(value);
+ value->release();
+
+ p = location + separator->length();
+ }
+
+ return result;
+}
+
+int String::intValue()
+{
+ return (int) strtol(UTF8Characters(), NULL, 10);
+}
+
+unsigned int String::unsignedIntValue()
+{
+ return (unsigned int) strtoul(UTF8Characters(), NULL, 10);
+}
+
+long String::longValue()
+{
+ return strtol(UTF8Characters(), NULL, 10);
+}
+
+unsigned long String::unsignedLongValue()
+{
+ return strtoul(UTF8Characters(), NULL, 10);
+}
+
+long long String::longLongValue()
+{
+ return strtoll(UTF8Characters(), NULL, 10);
+}
+
+unsigned long long String::unsignedLongLongValue()
+{
+ return strtoull(UTF8Characters(), NULL, 10);
+}
+
+Data * String::mUTF7EncodedData()
+{
+ return dataUsingEncoding("mutf-7");
+}
+
+String * String::stringWithMUTF7Data(Data * data)
+{
+ return data->stringWithCharset("mutf-7");
+}
+
+String * String::mUTF7EncodedString()
+{
+ Data * data = mUTF7EncodedData();
+ return data->stringWithCharset("ascii");
+}
+
+String * String::mUTF7DecodedString()
+{
+ Data * data = dataUsingEncoding("utf-8");
+ return stringWithMUTF7Data(data);
+}
+
+String * String::substringFromIndex(unsigned int idx)
+{
+ return substringWithRange(RangeMake(idx, length() - idx));
+}
+
+String * String::substringToIndex(unsigned int idx)
+{
+ return substringWithRange(RangeMake(0, idx));
+}
+
+String * String::substringWithRange(Range range)
+{
+ if (range.index > length()) {
+ return MCSTR("");
+ }
+
+ if (range.index + range.length > length()) {
+ range.length = length() - range.index;
+ }
+
+ return stringWithCharacters(unicodeCharacters() + range.index, range.length);
+}
+
+static chash * uniquedStringHash = NULL;
+
+static void initUniquedStringHash()
+{
+ uniquedStringHash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+}
+
+String * String::uniquedStringWithUTF8Characters(const char * UTF8Characters)
+{
+ chashdatum key;
+ chashdatum value;
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ int r;
+
+ pthread_once(&once, initUniquedStringHash);
+ key.data = (void *) UTF8Characters;
+ key.len = (unsigned int) strlen(UTF8Characters);
+ r = chash_get(uniquedStringHash, &key, &value);
+ if (r == 0) {
+ return (String *) value.data;
+ }
+ else {
+ value.data = new String(UTF8Characters);
+ value.len = 0;
+ chash_set(uniquedStringHash, &key, &value, NULL);
+ return (String *) value.data;
+ }
+}
diff --git a/src/core/basetypes/MCString.h b/src/core/basetypes/MCString.h
new file mode 100644
index 00000000..9ff04390
--- /dev/null
+++ b/src/core/basetypes/MCString.h
@@ -0,0 +1,123 @@
+#ifndef __MAILCORE_MCSTR_H_
+
+#define __MAILCORE_MCSTR_H_
+
+#include <mailcore/MCObject.h>
+#include <mailcore/MCRange.h>
+
+#include <stdarg.h>
+#include <unicode/utypes.h>
+
+namespace mailcore {
+
+ class Data;
+ class Array;
+
+ class String : public Object {
+ private:
+ UChar * mUnicodeChars;
+ unsigned int mLength;
+ unsigned int mAllocated;
+ void allocate(unsigned int length);
+ void appendCharactersLength(const UChar * unicodeCharacters, unsigned int length);
+ void reset();
+ int compareWithCaseSensitive(String * otherString, bool caseSensitive);
+ void appendBytes(const char * bytes, unsigned int length, const char * charset);
+ void appendUTF8CharactersLength(const char * UTF8Characters, unsigned int length);
+
+ public:
+ String(const UChar * unicodeChars = NULL);
+ String(const UChar * unicodeChars, unsigned int length);
+ String(const char * UTF8Characters);
+ String(String * otherString);
+ String(Data * data, const char * charset);
+ String(const char * bytes, unsigned int length, const char * charset = NULL);
+ virtual ~String();
+
+ //virtual String * className();
+ virtual String * description();
+ virtual Object * copy();
+ virtual bool isEqual(Object * otherObject);
+ virtual unsigned int hash();
+
+ static String * string();
+ static String * stringWithUTF8Format(const char * format, ...);
+ static String * stringWithVUTF8Format(const char * format, va_list ap);
+ static String * stringWithUTF8Characters(const char * UTF8Characters);
+ static String * stringWithCharacters(const UChar * characters);
+ static String * stringWithCharacters(const UChar * characters, unsigned int length);
+
+ virtual const UChar * unicodeCharacters();
+ virtual const char * UTF8Characters();
+ virtual unsigned int length();
+
+ virtual void appendString(String * otherString);
+ virtual void appendUTF8Format(const char * format, ...);
+ virtual void appendCharacters(const UChar * unicodeCharacters);
+ virtual void appendUTF8Characters(const char * UTF8Characters);
+ virtual void setString(String * otherString);
+ virtual void setUTF8Characters(const char * UTF8Characters);
+ virtual void setCharacters(const UChar * unicodeCharacters);
+
+ virtual String * stringByAppendingString(String * otherString);
+ virtual String * stringByAppendingUTF8Format(const char * format, ...);
+ virtual String * stringByAppendingUTF8Characters(const char * UTF8Characters);
+ virtual String * stringByAppendingCharacters(const UChar * unicodeCharacters);
+
+ virtual int compare(String * otherString);
+ virtual int caseInsensitiveCompare(String * otherString);
+ virtual String * lowercaseString();
+ virtual String * uppercaseString();
+
+ virtual UChar characterAtIndex(unsigned int idx);
+ virtual void deleteCharactersInRange(Range range);
+ virtual unsigned int replaceOccurrencesOfString(String * occurrence, String * replacement);
+ virtual int locationOfString(String * occurrence);
+
+ virtual Array * componentsSeparatedByString(String * separator);
+
+ // Additions
+ static String * stringByDecodingMIMEHeaderValue(const char * phrase);
+ virtual Data * encodedAddressDisplayNameValue();
+ virtual Data * encodedMIMEHeaderValue();
+ virtual Data * encodedMIMEHeaderValueForSubject();
+ virtual String * extractedSubject();
+ virtual String * extractedSubjectAndKeepBracket(bool keepBracket);
+ static String * uuidString();
+
+ virtual bool hasSuffix(String * suffix);
+ virtual bool hasPrefix(String * prefix);
+
+ virtual String * substringFromIndex(unsigned int idx);
+ virtual String * substringToIndex(unsigned int idx);
+ virtual String * substringWithRange(Range range);
+
+ virtual String * flattenHTML();
+ virtual String * flattenHTMLAndShowBlockquote(bool showBlockquote);
+ virtual String * flattenHTMLAndShowBlockquoteAndLink(bool showBlockquote, bool showLink);
+
+ virtual String * lastPathComponent();
+ virtual String * pathExtension();
+ virtual Data * dataUsingEncoding(const char * charset = NULL);
+
+ virtual const char * fileSystemRepresentation();
+ static String * stringWithFileSystemRepresentation(const char * filename);
+
+ int intValue();
+ unsigned int unsignedIntValue();
+ long longValue();
+ unsigned long unsignedLongValue();
+ long long longLongValue();
+ unsigned long long unsignedLongLongValue();
+
+ virtual Data * mUTF7EncodedData();
+ static String * stringWithMUTF7Data(Data * data);
+ virtual String * mUTF7EncodedString();
+ virtual String * mUTF7DecodedString();
+
+ static String * uniquedStringWithUTF8Characters(const char * UTF8Characters);
+ };
+
+}
+
+#endif
diff --git a/src/core/basetypes/MCUtils.h b/src/core/basetypes/MCUtils.h
new file mode 100644
index 00000000..8dbbf5f4
--- /dev/null
+++ b/src/core/basetypes/MCUtils.h
@@ -0,0 +1,34 @@
+#ifndef __MAILCORE_MCUTILS_H
+
+#define __MAILCORE_MCUTILS_H
+
+#define MC_SAFE_RETAIN(o) ((o) != NULL ? (o)->retain() : NULL)
+#define MC_SAFE_COPY(o) ((o) != NULL ? (o)->copy() : NULL)
+
+#define MC_SAFE_RELEASE(o) \
+ do { \
+ if ((o) != NULL) { \
+ (o)->release(); \
+ (o) = NULL; \
+ } \
+ } while (0)
+
+#define MC_SAFE_REPLACE_RETAIN(type, mField, value) \
+ do { \
+ MC_SAFE_RELEASE(mField); \
+ mField = (type *) MC_SAFE_RETAIN(value); \
+ } while (0)
+
+#define MC_SAFE_REPLACE_COPY(type, mField, value) \
+ do { \
+ MC_SAFE_RELEASE(mField); \
+ mField = (type *) MC_SAFE_COPY(value); \
+ } while (0)
+
+#define MCSTR(str) mailcore::String::uniquedStringWithUTF8Characters("" str "")
+
+#define MCUTF8(str) ((str) != NULL ? (str)->UTF8Characters() : NULL )
+#define MMCUTF8(str) MCUTF8(str)
+#define MCUTF8DESC(obj) ((obj) != NULL ? (obj)->description()->UTF8Characters() : NULL )
+
+#endif
diff --git a/src/core/basetypes/MCValue.cc b/src/core/basetypes/MCValue.cc
new file mode 100644
index 00000000..d275d3df
--- /dev/null
+++ b/src/core/basetypes/MCValue.cc
@@ -0,0 +1,314 @@
+#include "MCValue.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "MCString.h"
+#include "MCHash.h"
+#include "MCUtils.h"
+
+using namespace mailcore;
+
+enum {
+ VALUE_TYPE_NONE,
+ VALUE_TYPE_BOOL_VALUE,
+ VALUE_TYPE_CHAR_VALUE,
+ VALUE_TYPE_UNSIGNED_CHAR_VALUE,
+ VALUE_TYPE_SHORT_VALUE,
+ VALUE_TYPE_UNSIGNED_SHORT_VALUE,
+ VALUE_TYPE_INT_VALUE,
+ VALUE_TYPE_UNSIGNED_INT_VALUE,
+ VALUE_TYPE_LONG_VALUE,
+ VALUE_TYPE_UNSIGNED_LONG_VALUE,
+ VALUE_TYPE_LONG_LONG_VALUE,
+ VALUE_TYPE_UNSIGNED_LONG_LONG_VALUE,
+ VALUE_TYPE_FLOAT_VALUE,
+ VALUE_TYPE_DOUBLE_VALUE,
+ VALUE_TYPE_POINTER_VALUE,
+ VALUE_TYPE_DATA_VALUE,
+};
+
+Value::Value()
+{
+ mType = VALUE_TYPE_NONE;
+ memset(&mValue, 0, sizeof(mValue));
+}
+
+Value::Value(Value * other)
+{
+ memcpy(&mValue, &other->mValue, sizeof(mValue));
+ mType = other->mType;
+ if (mType == VALUE_TYPE_DATA_VALUE) {
+ mValue.dataValue.data = (char *) malloc(mValue.dataValue.length);
+ memcpy(mValue.dataValue.data, other->mValue.dataValue.data, mValue.dataValue.length);
+ }
+}
+
+Value::~Value()
+{
+ if (mType == VALUE_TYPE_DATA_VALUE) {
+ free(mValue.dataValue.data);
+ }
+}
+
+String * Value::description()
+{
+ switch (mType) {
+ case VALUE_TYPE_BOOL_VALUE:
+ if (mValue.boolValue) {
+ return MCSTR("true");
+ }
+ else {
+ return MCSTR("false");
+ }
+ case VALUE_TYPE_CHAR_VALUE:
+ return String::stringWithUTF8Format("%i", (int) mValue.charValue);
+ case VALUE_TYPE_UNSIGNED_CHAR_VALUE:
+ return String::stringWithUTF8Format("%u", (unsigned int) mValue.unsignedCharValue);
+ case VALUE_TYPE_SHORT_VALUE:
+ return String::stringWithUTF8Format("%i", (int) mValue.shortValue);
+ case VALUE_TYPE_UNSIGNED_SHORT_VALUE:
+ return String::stringWithUTF8Format("%u", (unsigned int) mValue.unsignedShortValue);
+ case VALUE_TYPE_INT_VALUE:
+ return String::stringWithUTF8Format("%i", mValue.intValue);
+ case VALUE_TYPE_UNSIGNED_INT_VALUE:
+ return String::stringWithUTF8Format("%u", mValue.unsignedIntValue);
+ case VALUE_TYPE_LONG_VALUE:
+ return String::stringWithUTF8Format("%li", mValue.longValue);
+ case VALUE_TYPE_UNSIGNED_LONG_VALUE:
+ return String::stringWithUTF8Format("%lu", mValue.unsignedLongValue);
+ case VALUE_TYPE_LONG_LONG_VALUE:
+ return String::stringWithUTF8Format("%lli", mValue.longLongValue);
+ case VALUE_TYPE_UNSIGNED_LONG_LONG_VALUE:
+ return String::stringWithUTF8Format("%llu", mValue.unsignedLongLongValue);
+ case VALUE_TYPE_FLOAT_VALUE:
+ return String::stringWithUTF8Format("%f", (double) mValue.floatValue);
+ case VALUE_TYPE_DOUBLE_VALUE:
+ return String::stringWithUTF8Format("%f", mValue.doubleValue);
+ case VALUE_TYPE_POINTER_VALUE:
+ return String::stringWithUTF8Format("%p", mValue.pointerValue);
+ case VALUE_TYPE_DATA_VALUE:
+ return String::stringWithUTF8Format("<Value:%p:data>", this);
+ default:
+ return String::stringWithUTF8Format("<Value:%p:unknown>", this);
+ }
+}
+
+#if 0
+String * Value::className()
+{
+ return MCSTR("Value");
+}
+#endif
+
+bool Value::isEqual(Object * otherObject)
+{
+ Value * otherValue = (Value *) otherObject;
+ if (otherValue->mType != mType)
+ return false;
+ if (mType == VALUE_TYPE_DATA_VALUE) {
+ if (mValue.dataValue.length != otherValue->mValue.dataValue.length)
+ return false;
+ if (memcmp(&otherValue->mValue.dataValue.data, &mValue.dataValue.data, mValue.dataValue.length) != 0)
+ return false;
+ }
+ else {
+ if (memcmp(&otherValue->mValue, &mValue, sizeof(mValue)) != 0)
+ return false;
+ }
+ return true;
+}
+
+unsigned int Value::hash()
+{
+ return hashCompute((const char *) &mValue, sizeof(mValue));
+}
+
+Object * Value::copy()
+{
+ return new Value(this);
+}
+
+Value * Value::valueWithBoolValue(bool value)
+{
+ Value * result = new Value();
+ result->mType = VALUE_TYPE_BOOL_VALUE;
+ result->mValue.boolValue = value;
+ return (Value *) result->autorelease();
+}
+
+Value * Value::valueWithCharValue(char value)
+{
+ Value * result = new Value();
+ result->mType = VALUE_TYPE_CHAR_VALUE;
+ result->mValue.charValue = value;
+ return (Value *) result->autorelease();
+}
+
+Value * Value::valueWithUnsignedCharValue(unsigned char value)
+{
+ Value * result = new Value();
+ result->mType = VALUE_TYPE_UNSIGNED_CHAR_VALUE;
+ result->mValue.unsignedCharValue = value;
+ return (Value *) result->autorelease();
+}
+
+///////////////////////
+
+Value * Value::valueWithIntValue(int value)
+{
+ Value * result = new Value();
+ result->mType = VALUE_TYPE_INT_VALUE;
+ result->mValue.intValue = value;
+ return (Value *) result->autorelease();
+}
+
+Value * Value::valueWithUnsignedIntValue(unsigned int value)
+{
+ Value * result = new Value();
+ result->mType = VALUE_TYPE_UNSIGNED_INT_VALUE;
+ result->mValue.unsignedIntValue = value;
+ return (Value *) result->autorelease();
+}
+
+Value * Value::valueWithLongValue(long value)
+{
+ Value * result = new Value();
+ result->mType = VALUE_TYPE_LONG_VALUE;
+ result->mValue.longValue = value;
+ return (Value *) result->autorelease();
+}
+
+Value * Value::valueWithUnsignedLongValue(unsigned long value)
+{
+ Value * result = new Value();
+ result->mType = VALUE_TYPE_UNSIGNED_LONG_VALUE;
+ result->mValue.unsignedLongValue = value;
+ return (Value *) result->autorelease();
+}
+
+Value * Value::valueWithLongLongValue(long long value)
+{
+ Value * result = new Value();
+ result->mType = VALUE_TYPE_LONG_LONG_VALUE;
+ result->mValue.longLongValue = value;
+ return (Value *) result->autorelease();
+}
+
+Value * Value::valueWithUnsignedLongLongValue(unsigned long long value)
+{
+ Value * result = new Value();
+ result->mType = VALUE_TYPE_UNSIGNED_LONG_LONG_VALUE;
+ result->mValue.unsignedLongLongValue = value;
+ return (Value *) result->autorelease();
+}
+
+Value * Value::valueWithFloatValue(float value)
+{
+ Value * result = new Value();
+ result->mType = VALUE_TYPE_FLOAT_VALUE;
+ result->mValue.floatValue = value;
+ return (Value *) result->autorelease();
+}
+
+Value * Value::valueWithDoubleValue(double value)
+{
+ Value * result = new Value();
+ result->mType = VALUE_TYPE_DOUBLE_VALUE;
+ result->mValue.doubleValue = value;
+ return (Value *) result->autorelease();
+}
+
+Value * Value::valueWithPointerValue(void * value)
+{
+ Value * result = new Value();
+ result->mType = VALUE_TYPE_POINTER_VALUE;
+ result->mValue.pointerValue = value;
+ return (Value *) result->autorelease();
+}
+
+Value * Value::valueWithData(const char * value, int length)
+{
+ Value * result = new Value();
+ result->mType = VALUE_TYPE_DATA_VALUE;
+ result->mValue.dataValue.data = (char *) malloc(length);
+ memcpy(result->mValue.dataValue.data, value, length);
+ result->mValue.dataValue.length = length;
+ return (Value *) result->autorelease();
+}
+
+bool Value::boolValue()
+{
+ return mValue.boolValue;
+}
+
+char Value::charValue()
+{
+ return mValue.charValue;
+}
+
+unsigned char Value::unsignedCharValue()
+{
+ return mValue.unsignedCharValue;
+}
+
+short Value::shortValue()
+{
+ return mValue.shortValue;
+}
+
+unsigned short Value::unsignedShortValue()
+{
+ return mValue.unsignedShortValue;
+}
+
+int Value::intValue()
+{
+ return mValue.intValue;
+}
+
+unsigned int Value::unsignedIntValue()
+{
+ return mValue.unsignedIntValue;
+}
+
+long Value::longValue()
+{
+ return mValue.longValue;
+}
+
+unsigned long Value::unsignedLongValue()
+{
+ return mValue.unsignedLongValue;
+}
+
+long long Value::longLongValue()
+{
+ return mValue.longLongValue;
+}
+
+unsigned long long Value::unsignedLongLongValue()
+{
+ return mValue.unsignedLongLongValue;
+}
+
+float Value::floatValue()
+{
+ return mValue.floatValue;
+}
+
+double Value::doubleValue()
+{
+ return mValue.doubleValue;
+}
+
+void * Value::pointerValue()
+{
+ return mValue.pointerValue;
+}
+
+void Value::dataValue(const char ** p_value, int * p_length)
+{
+ * p_value = mValue.dataValue.data;
+ * p_length = mValue.dataValue.length;
+}
diff --git a/src/core/basetypes/MCValue.h b/src/core/basetypes/MCValue.h
new file mode 100644
index 00000000..cc776b24
--- /dev/null
+++ b/src/core/basetypes/MCValue.h
@@ -0,0 +1,81 @@
+#ifndef __MAILCORE_MCVALUE_H_
+
+#define __MAILCORE_MCVALUE_H_
+
+#include <mailcore/MCObject.h>
+
+namespace mailcore {
+
+ class String;
+
+ class Value : public Object {
+ private:
+ int mType;
+ union {
+ bool boolValue;
+ char charValue;
+ unsigned char unsignedCharValue;
+ short shortValue;
+ unsigned short unsignedShortValue;
+ int intValue;
+ unsigned int unsignedIntValue;
+ long longValue;
+ unsigned long unsignedLongValue;
+ long long longLongValue;
+ unsigned long long unsignedLongLongValue;
+ float floatValue;
+ double doubleValue;
+ void * pointerValue;
+ struct {
+ char * data;
+ int length;
+ } dataValue;
+ } mValue;
+ Value();
+
+ public:
+ Value(Value * other);
+ virtual ~Value();
+
+ virtual String * description();
+ //virtual String * className();
+
+ virtual bool isEqual(Object * otherObject);
+ virtual unsigned int hash();
+
+ Object * copy();
+
+ static Value * valueWithBoolValue(bool value);
+ static Value * valueWithCharValue(char value);
+ static Value * valueWithUnsignedCharValue(unsigned char value);
+ static Value * valueWithIntValue(int value);
+ static Value * valueWithUnsignedIntValue(unsigned int value);
+ static Value * valueWithLongValue(long value);
+ static Value * valueWithUnsignedLongValue(unsigned long value);
+ static Value * valueWithLongLongValue(long long value);
+ static Value * valueWithUnsignedLongLongValue(unsigned long long value);
+ static Value * valueWithFloatValue(float value);
+ static Value * valueWithDoubleValue(double value);
+ static Value * valueWithPointerValue(void * value);
+ static Value * valueWithData(const char * value, int length);
+
+ virtual bool boolValue();
+ virtual char charValue();
+ virtual unsigned char unsignedCharValue();
+ virtual short shortValue();
+ virtual unsigned short unsignedShortValue();
+ virtual int intValue();
+ virtual unsigned int unsignedIntValue();
+ virtual long longValue();
+ virtual unsigned long unsignedLongValue();
+ virtual long long longLongValue();
+ virtual unsigned long long unsignedLongLongValue();
+ virtual float floatValue();
+ virtual double doubleValue();
+ virtual void * pointerValue();
+ virtual void dataValue(const char ** p_value, int * p_length);
+ };
+
+}
+
+#endif