diff options
author | 2013-01-11 01:08:18 -0800 | |
---|---|---|
committer | 2013-01-11 01:08:18 -0800 | |
commit | 739b68a69682d80d8247d4465eae7b182acc9da0 (patch) | |
tree | 34c33738bc0761d30c13f7f9b88fdf0d5c8cb4a9 /src/core/basetypes | |
parent | f83e0e9ba3da2b8887f849483506d5de8f1d2c54 (diff) |
first commit
Diffstat (limited to 'src/core/basetypes')
34 files changed, 4756 insertions, 0 deletions
diff --git a/src/core/basetypes/.DS_Store b/src/core/basetypes/.DS_Store Binary files differnew file mode 100644 index 00000000..5008ddfc --- /dev/null +++ b/src/core/basetypes/.DS_Store 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 |