commit a3229a051381e8f6b6df0fd423186166d20c898f Author: Vincent Richard Date: Tue Oct 5 10:28:21 2004 +0000 Initial import. diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..e69de29b diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..d05be1bd --- /dev/null +++ b/ChangeLog @@ -0,0 +1,434 @@ + +VERSION 0.5.2-CVS +================= + +2004-09-09 Vincent Richard + + * IMAPFolder.cpp: fixed rename(): folder name is now updated. + +2004-08-21 Vincent Richard + + * charset.cpp: workaround (hack?) for different 'iconv' prototypes (they + may differ in the second parameter being 'const' or not). + +2004-08-20 Vincent Richard + + * renamed "messaging/folderPath" to "utility/path" for common use in + "messaging/folder" and "utility/file". + + * moved "stream" and "stringProxy" into "utility" namespace. + + * started to write some "JavaDoc-like" comments, for use with Doxygen. + +2004-08-18 Vincent Richard + + * stringProxy.hpp: fixed stringProxy::it_end() which returned wrong + value (typo...). + +2004-07-26 Vincent Richard + + * fileAttachment: fixed the encoding param (not set to default anymore) + and provided a new constructor to specify your own encoding. + +2004-07-22 Vincent Richard + + * wide-char support is disabled by default. To enable, set the flag + "with_wide_char_support=yes" on the SCons command line. + +2004-07-08 Vincent Richard + + * renamed messaging/POP3*, messaging/IMAP* and messaging/SMTP* classes + to follow the same convention as other class names. + +2004-07-03 Vincent Richard + + * moved some files to "utility" subdirectory ("vmime::utility" namespace). + + +VERSION 0.5.1 +============= + +2004-06-15 Vincent Richard + + * contentHandler, htmlTextPart: Fixed some compilation issues with + g++ version < 3.4: + [error: declaration of `const vmime::encoding& encoding() const' + changes meaning of `encoding' from `class vmime::encoding']. + + * Fixed errors in SConstruct with Windows NT (2k, XP...). + + +VERSION 0.5.0 +============= + +2004-05-26 Vincent Richard + + * added methods receiveRaw() and sendRaw() on vmime::socket object. Do not + forget to implement it, or you will get a compile error. + +2004-05-21 Vincent Richard + + * added some unit tests in the "tests" directory. To run all the tests, 'cd' + to the "tests" directory, compile test programs by running "make" and then + execute the "run-tests.sh" script. + + * charset: added a convert() function to perform stream conversion. + +2004-05-18 Vincent Richard + + * encoder*: updated all encoders so they use input streams and output + streams instead of a in-memory string. You can use the stream adapters + (inputStreamStringAdapter and outputStreamStringAdapter) for your code + to continue working the old-fashioned way... + +2004-05-17 Vincent Richard + + * messaging/transport.hpp: added a "size" parameter to send() function. + +2004-05-16 Vincent Richard + + * body: body contents and data in text parts are now handled via a + proxy object: contentHandler. This allow more flexibility, including + providing data from an input stream instead of storing whole data in + memory into a string object. This also provide a big performance and + memory usage improvement. For more information, please see the comments + in the file "contentHandler.hpp". + +2004-05-15 Vincent Richard + + * all files: modified the parsing in depth (not using iterators anymore), + the code is clearer and faster. + + * IMAPutils.cpp: corrected a bug (typo) in IMAPutils::dateTime(). + +2004-05-13 Vincent Richard + + * all files: added a generate() method on vmime::component to generate + objects into an output stream (outputStream). This offers a large + performance and memory usage improvement when generating big messages. + + * stream.cpp/.hpp: new objects "inputStream" and "outputStream" to + provide more flexibility than with standard C++ streams. There are + also adapters for standard i/o streams, provided for compatibility. + + +VERSION 0.4.2 +============= + +2004-05-08 Vincent Richard + + * messaging: added a system of event notification (message change, + folder renamed, etc...). For more information about this, please + consult "src/messaging/events.hpp". + +2004-05-03 Vincent Richard + + * messaging: added a lot of useful features to message stores + (set/get message flags, message deletion, copy, rename folder, + adding messages, unique identifiers, MIME part/header fetch, + partial fetch...). + +2004-04-30 Vincent Richard + + * messaging/message.hpp: added a fetchPartHeader() method to + extract the header of a specific MIME part. + +2004-04-25 Vincent Richard + + * all files: removed (illegal) extra ';' after namespace + declarations. + + * all files: fixed some compilation errors with g++-3.4 (the + parser is more strict and more standard-compliant). + +2004-04-24 Vincent Richard + + * messaging/*: splitted "progressListener" into two objects: + "progressionListener" and "timeoutHandler". The last one is + used internally in VMime. The "progressionListener" parameter + is no more passed as argument to the constructor of a "service" + object. Instead, it can be given in argument to the functions + that use it: + - message::extract[Part]() + - folder::fetchMessages() + - transport::send() + +2004-04-04 Vincent Richard + + * messaging/folder.hpp: added a (optional) parameter "recursive" + to getFolders() to allow enumeration of all sub-folders + (that is, direct and indirect). + +2004-04-03 Vincent Richard + + * messaging/authenti[fi]cationInfos: renamed class + 'authentificationInfos' to 'authenticationInfos'. + + * exception.hpp: renamed class 'authentification_error' to + 'authentication_error'. + + * messaging/SMTPtransport: renamed 'options.need-authentification' + to 'options.need-authentication'. + +2004-04-02 Vincent Richard + + * added basic IMAP support. This is EXPERIMENTAL. + +2004-03-25 Vincent Richard + + * messaging::folder::path: changed type of 'component' from 'string' + to 'word' to allow multiple charsets to be used in a path. + + * implemented a noop() command on vmime::messaging::service class. + + * messageParser.cpp: it is now possible to get more information on an + attachment using the "Content-Disposition" (use the attachmentInfo() + fonction to retrieve the "Content-Disposition" field related to + the attachment). + + +VERSION 0.4.1 +============= + +2004-03-24 Vincent Richard + + * SMTPtransport.cpp: fixed a bug in send(). + + +VERSION 0.4.0 +============= + +2004-02-19 Vincent Richard + + * mailboxGroup.cpp: fixed a segfault when generating() an empty group + (eg. "undisclosed-recipient"). + +2004-02-17 Vincent Richard + + * === MAJOR CHANGE === Removed old "network features". Now, this is called + "messaging system" and a new (incompatible) interface is provided. + +2003-12-30 Vincent Richard + + * encoderFactory.cpp/.hpp: added stuff to allow iterating through + registered encoders. + + * encoder*.cpp/.hpp: changed the way options/results are set in encoders: + now, a vmime::propertySet is used. This provides more flexibility. + +2003-12-25 Vincent Richard + + * constants.cpp/.hpp: media types constants: removed "sub" namespace and + translated "sub::[TYPE]::[SUBTYPE]" to "[TYPE]_[SUBTYPE]". + +2003-12-08 Vincent Richard + + * constants.cpp/.hpp, dateTime.cpp/.hpp: translated all constants/enums + from lower-case to upper-case letters. + +2003-12-04 Vincent Richard + + * Created a new class for singleton. Derived all concerned class from + this new class. This concerns: "encoderFactory", "headerFieldFactory", + "parameterFactory", "options" and "textPartFactory". + +2003-12-02 Vincent Richard + + * Moved to SCons building system (http://www.scons.org/) and dropped old + autoconf/automake system. Type 'scons' to build the library and use + 'scons install' to install it on your system. + +2003-12-01 Vincent Richard + + * mailboxGroup.cpp: fixed a bug in typeid() comparison: changed + "typeid(parsedAddress)" to "typeid(*parsedAddress)" to test the + object dynamic type (the previous test was always false). + + +VERSION 0.3.5 +============= + +2003-10-24 Vincent Richard + + * included some sample programs in the "examples/" directory. For a more + complete documentation, please visit: http://www.kisli.com/vmime/doc/ . + + * all files: it is not possible to create header fields directly anymore + (ie. you cannot call the constructor directly); instead, you should use + the "headerFieldFactory" object. + + +VERSION 0.3.4 +============= + +2003-10-05 Vincent Richard + + * all files: changed all calls 'std::isspace(???)' to '[vmime::]isspace(???)' + since no locale was passed (anyway, no locale is needed: text is ASCII). + +2003-10-04 Kai Stammerjohann + + * included a Visual C++ 7 solution/project for vmime: see "vmime.sln" and + "vmime.vcproj" in the root directory. + + +VERSION 0.3.3 +============= + +2003-09-22 Vincent Richard + + * moved all constants (media types, charsets...) from base.cpp/.hpp to new + files constants.cpp/.hpp. + +2003-09-21 Vincent Richard + + * messageBuilder.cpp (construct): fixed algorithm for generating text parts. + Single and multiple text parts, with or without attachments are now handled + correctly (as recommended by the RFCs). + + * bodyPart.cpp/.hpp, body.cpp/.hpp, header.cpp/.hpp: added clone() and + operator=() functions to be able to duplicate body parts. + + * messageParser.cpp (findTextParts): handled the case in which the message + is not "multipart/*": we use the main part if its type is "text/*". + + * messageParser.cpp (destructor): added code for deleting the text parts + created by the findTextParts() function. + + +VERSION 0.3.2 +============= + +2003-09-19 Bevan Collins + + * encoderQP.cpp: fixed a bug in quoted-printable encoding: "=20\r\n" is + appended to the line ending with a space. + +2003-09-13 Vincent Richard + + * charset.cpp/.hpp: dropped internal conversion from charset name (string) to + charset type (enum). We keep only the name of the charset. + + * base.cpp/.hpp: added string constants for some charsets. + +2003-09-12 Vincent Richard + + * messageParser.cpp (findAttachments): fixed the search for attachment + parts. The right test is "cdf.value().name() != dispositionTypes::_inline" + and not "cdf.value().name() != dispositionTypes::attachment"... + +2003-09-11 Vincent Richard + + * plainTextPart.cpp/htmlTextPart.cpp: fixed a bug in parse(): when getting + the "charset" parameter, "no_such_parameter" exception was not caught if + the parameter was not present. + +2003-09-06 Vincent Richard + + * base.cpp: added a special case when encoding to Quoted-Printable: lines + are no more limited to 76 characters (the maximum length recommended by + the RFC) if maxLineLength == lineLengthLimits::infinite. However, this + SHOULD NOT be used when generating mails (not RFC compliant). + + +VERSION 0.3.1 +============= + +2003-08-24 Vincent Richard + + * mailbox.hpp: added "const" functions for name() and email(). + +2003-07-26 Vincent Richard + + * charset.cpp: fixed a bug in "charset::iconvert()". Also, the + conversion is now done using a buffer, and not in one block. + +2003-07-24 Vincent Richard + + * receiveProtocol[POP3].hpp/.cpp: a socket factory can now be passed in + argument to the constructor so that it is possible to override the + use of the default factory (set in vmime::platformDependantHandler). + + +VERSION 0.3.0 +============= + +2003-07-21 Vincent Richard + + * configure.in: changed 'libdir' to install lib files in {PREFIX}/lib + instead of {PREFIX}/lib/vmime. However, include files remain in the + {PREFIX}/include/vmime directory. + +2003-06-28 Vincent Richard + + * base.hpp/.cpp: changed the return type of "libname()" and "libversion()" + from "string::value_type*" to "string". + +2003-06-16 Vincent Richard + + * platformDependant.hpp: added "getSocketFactory()" function to be used + with the new network features. + + * configure.in: modified the file to permit passing arguments to + the "configure" script: + . --disable-net: disable network support (new in 0.3.0) + . --enable-debug: enable debug mode (not used for now) + + * started version 0.3.0: added network features: connection to mail + servers via POP3, IMAP... Related classes: "receiveProtocol*", + "serverInfos" and "socket", an abstract socket class. + + +VERSION 0.2.1 +============= + +2003-05-28 Vincent Richard + + * messageId.cpp: added "vmime." at the beginning of random-generated + message-ids (to make them more unique). + +2003-05-26 Vincent Richard + + * all source files: replaced "_VMIME_xxxxx_HPP_INCLUDED_" macros with + "VMIME_xxxxx_HPP_INCLUDED". Names beginning with "_" (underscore) and + followed by an uppercase letter are reserved to the implementation + (see the C++ standard: 17.4.3.1.2 Global names). + + +VERSION 0.2.0 +============= + +2003-05-18 Vincent Richard + + * messageParser.cpp: added a message parser (to be used parallely with + messageBuilder). Extraction of attachment, plain text parts and HTML + text parts (with embedded objects) is supported. + +2003-05-09 Vincent Richard + + * body.cpp (generate): the default prolog & epilog text (as defined + in vmime::options) are not written anymore in sub-parts (only for + the "root" part). Added a "isRoot" member to vmime::header. + +2003-05-08 Vincent Richard + + * encoding.cpp (decide): added some code to choose "quoted-printable" + when there are lines with more than "lineLengthLimits::convenient" + characters (or with a '.' just after a '\n'). + + * base.cpp (makeWordsFromText): enhanced algorithm. + +2003-05-04 Vincent Richard + + * address.cpp, mailbox.cpp, mailboxGroup.cpp: added empty() function. + + * messageBuilder.cpp (construct): some validity checks: we now check + there is one expeditor and at least one recipient. + + +VERSION 0.1.0 +============= + +2003-05-03 Vincent Richard + + * First (beta) version released. diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..e69de29b diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/README b/README new file mode 100644 index 00000000..6921ceac --- /dev/null +++ b/README @@ -0,0 +1,2 @@ + +TODO diff --git a/SConstruct b/SConstruct new file mode 100644 index 00000000..1bbd0662 --- /dev/null +++ b/SConstruct @@ -0,0 +1,688 @@ +# +# SConstruct +# libvmime build script +# +# Process this file with 'scons' to build the project. +# For more information, please visit: http://www.scons.org/ . +# +# Usage: +# +# . scons build the library +# . scons -h see available configuration options +# . scons opt=value set a configuration option +# . scons install install library and include files (as root) +# . scons dist build a source package (.tar.bz2) +# + +import commands +import os +import sys +import re +import string + + +################## +# Source files # +################## + +libvmime_sources = [ + 'address.cpp', 'address.hpp', + 'addressList.cpp', 'addressList.hpp', + 'addressListField.cpp', 'addressListField.hpp', + 'attachment.hpp', + 'base.cpp', 'base.hpp', + 'body.cpp', 'body.hpp', + 'bodyPart.cpp', 'bodyPart.hpp', + 'charset.cpp', 'charset.hpp', + 'charsetParameter.cpp', 'charsetParameter.hpp', + 'component.cpp', 'component.hpp', + 'constants.cpp', 'constants.hpp', + 'contentDispositionField.cpp', 'contentDispositionField.hpp', + 'contentEncodingField.cpp', 'contentEncodingField.hpp', + 'contentHandler.cpp', 'contentHandler.hpp', + 'contentTypeField.cpp', 'contentTypeField.hpp', + 'dateField.cpp', 'dateField.hpp', + 'dateParameter.cpp', 'dateParameter.hpp', + 'dateTime.cpp', 'dateTime.hpp', + 'defaultAttachment.cpp', 'defaultAttachment.hpp', + 'defaultField.cpp', 'defaultField.hpp', + 'defaultParameter.cpp', 'defaultParameter.hpp', + 'defaultParameterizedHeaderField.cpp', 'defaultParameterizedHeaderField.hpp', + 'disposition.cpp', 'disposition.hpp', + 'encoder.cpp', 'encoder.hpp', + 'encoder7bit.cpp', 'encoder7bit.hpp', + 'encoder8bit.cpp', 'encoder8bit.hpp', + 'encoderB64.cpp', 'encoderB64.hpp', + 'encoderBinary.cpp', 'encoderBinary.hpp', + 'encoderDefault.cpp', 'encoderDefault.hpp', + 'encoderFactory.cpp', 'encoderFactory.hpp', + 'encoderQP.cpp', 'encoderQP.hpp', + 'encoderUUE.cpp', 'encoderUUE.hpp', + 'encoding.cpp', 'encoding.hpp', + 'exception.hpp', + 'fileAttachment.cpp', 'fileAttachment.hpp', + 'header.cpp', 'header.hpp', + 'headerFieldFactory.cpp', 'headerFieldFactory.hpp', + 'headerField.cpp', 'headerField.hpp', + 'htmlTextPart.cpp', 'htmlTextPart.hpp', + 'mailbox.cpp', 'mailbox.hpp', + 'mailboxField.cpp', 'mailboxField.hpp', + 'mailboxGroup.cpp', 'mailboxGroup.hpp', + 'mailboxList.cpp', 'mailboxList.hpp', + 'mailboxListField.cpp', 'mailboxListField.hpp', + 'mediaType.cpp', 'mediaType.hpp', + 'messageBuilder.cpp', 'messageBuilder.hpp', + 'message.cpp', 'message.hpp', + 'messageId.cpp', 'messageId.hpp', + 'messageIdField.cpp', 'messageIdField.hpp', + 'messageParser.cpp', 'messageParser.hpp', + 'options.cpp', 'options.hpp', + 'parameter.cpp', 'parameter.hpp', + 'parameterFactory.cpp', 'parameterFactory.hpp', + 'parameterizedHeaderField.cpp', 'parameterizedHeaderField.hpp', + 'parserHelpers.hpp', + 'plainTextPart.cpp', 'plainTextPart.hpp', + 'platformDependant.cpp', 'platformDependant.hpp', + 'propertySet.cpp', 'propertySet.hpp', + 'relayField.cpp', 'relayField.hpp', + 'text.cpp', 'text.hpp', + 'textField.cpp', 'textField.hpp', + 'textParameter.cpp', 'textParameter.hpp', + 'textPartFactory.cpp', 'textPartFactory.hpp', + 'textPart.hpp', + 'types.hpp', + 'word.cpp', 'word.hpp', + 'vmime', + 'utility/file.hpp', + 'utility/md5.cpp', 'utility/md5.hpp', + 'utility/path.cpp', 'utility/path.hpp', + 'utility/random.cpp', 'utility/random.hpp', + 'utility/singleton.cpp', 'utility/singleton.hpp', + 'utility/smartPtr.hpp', + 'utility/stream.cpp', 'utility/stream.hpp', + 'utility/stringProxy.cpp', 'utility/stringProxy.hpp' +] + +libvmime_examples_sources = [ + 'examples/common.inc', + 'examples/README', + 'examples/example1.cpp', + 'examples/example2.cpp', + 'examples/example3.cpp', + 'examples/example4.cpp', + 'examples/example5.cpp', + 'examples/example6.cpp' +] + +libvmime_messaging_sources = [ + 'messaging/authenticator.cpp', 'messaging/authenticator.hpp', + 'messaging/authenticationInfos.cpp', 'messaging/authenticationInfos.hpp', + 'messaging/authHelper.cpp', 'messaging/authHelper.hpp', + 'messaging/builtinServices.inl', + 'messaging/defaultAuthenticator.cpp', 'messaging/defaultAuthenticator.hpp', + 'messaging/events.cpp', 'messaging/events.hpp', + 'messaging/folder.cpp', 'messaging/folder.hpp', + 'messaging/message.cpp', 'messaging/message.hpp', + 'messaging/progressionListener.hpp', + 'messaging/service.cpp', 'messaging/service.hpp', + 'messaging/serviceFactory.cpp', 'messaging/serviceFactory.hpp', + 'messaging/serviceInfos.hpp', + 'messaging/session.cpp', 'messaging/session.hpp', + 'messaging/simpleAuthenticator.cpp', 'messaging/simpleAuthenticator.hpp', + 'messaging/socket.hpp', + 'messaging/store.hpp', + 'messaging/timeoutHandler.hpp', + 'messaging/transport.hpp', + 'messaging/url.cpp', 'messaging/url.hpp', + 'messaging/urlUtils.cpp', 'messaging/urlUtils.hpp' +] + +libvmime_messaging_proto_sources = [ + [ + 'pop3', + [ + 'messaging/POP3Store.cpp', 'messaging/POP3Store.hpp', + 'messaging/POP3Folder.cpp', 'messaging/POP3Folder.hpp', + 'messaging/POP3Message.cpp', 'messaging/POP3Message.hpp' + ] + ], + [ + 'smtp', + [ + 'messaging/SMTPTransport.cpp', 'messaging/SMTPTransport.hpp' + ] + ], + [ + 'imap', + [ + 'messaging/IMAPConnection.cpp', 'messaging/IMAPConnection.hpp', + 'messaging/IMAPStore.cpp', 'messaging/IMAPStore.hpp', + 'messaging/IMAPFolder.cpp', 'messaging/IMAPFolder.hpp', + 'messaging/IMAPMessage.cpp', 'messaging/IMAPMessage.hpp', + 'messaging/IMAPTag.cpp', 'messaging/IMAPTag.hpp', + 'messaging/IMAPUtils.cpp', 'messaging/IMAPUtils.hpp', + 'messaging/IMAPParser.hpp' + ] + ], + [ + 'maildir', + [ + 'messaging/maildirStore.cpp', 'messaging/maildirStore.hpp', + 'messaging/maildirFolder.cpp', 'messaging/maildirFolder.hpp', + 'messaging/maildirMessage.cpp', 'messaging/maildirMessage.hpp', + 'messaging/maildirUtils.cpp', 'messaging/maildirUtils.hpp' + ] + ] +] + +libvmime_extra = [ + 'AUTHORS', + 'ChangeLog', + 'COPYING', + 'INSTALL', + 'NEWS', + 'README', + 'SConstruct', + 'VERSION' +] + +libvmime_tests = [ + # charset + 'tests/charset/Makefile', + 'tests/charset/main.cpp', + 'tests/charset/run-test.sh', + 'tests/charset/test-suites/gnu.in.utf-8', + 'tests/charset/test-suites/gnu.out.iso-8859-1', + + # encoding + 'tests/encoding/Makefile', + 'tests/encoding/main.cpp', + 'tests/encoding/run-test.sh', + 'tests/encoding/test-suites/encode/1byte', + 'tests/encoding/test-suites/encode/1byte.base64', + 'tests/encoding/test-suites/encode/2bytes', + 'tests/encoding/test-suites/encode/2bytes.base64', + 'tests/encoding/test-suites/encode/3bytes', + 'tests/encoding/test-suites/encode/3bytes.base64', + 'tests/encoding/test-suites/encode/empty', + 'tests/encoding/test-suites/encode/empty.base64', + 'tests/encoding/test-suites/encode/empty.quoted-printable', + 'tests/encoding/test-suites/encode/gpl', + 'tests/encoding/test-suites/encode/gpl.base64', + 'tests/encoding/test-suites/encode/gpl.quoted-printable', + 'tests/encoding/test-suites/encode/gpl.uuencode', + 'tests/encoding/test-suites/encode/ls', + 'tests/encoding/test-suites/encode/ls.base64', + 'tests/encoding/test-suites/encode/ls.quoted-printable', + 'tests/encoding/test-suites/encode/ls.uuencode', + 'tests/encoding/test-suites/encode/simple', + 'tests/encoding/test-suites/encode/simple.base64', + 'tests/encoding/test-suites/encode/simple.quoted-printable', + 'tests/encoding/test-suites/encode/simple.uuencode', + 'tests/encoding/test-suites/decode/ls', + 'tests/encoding/test-suites/decode/ls.base64', + + # mailbox + 'tests/mailbox/Makefile', + 'tests/mailbox/main.cpp', + 'tests/mailbox/run-test.sh', + 'tests/mailbox/test-suites/test1.in', + 'tests/mailbox/test-suites/test1.out', + 'tests/mailbox/test-suites/test2.in', + 'tests/mailbox/test-suites/test2.out', + + # main + 'tests/Makefile', + 'tests/run-tests.sh' +] + +libvmime_dist_files = libvmime_sources + libvmime_messaging_sources + +for i in range(len(libvmime_dist_files)): + libvmime_dist_files[i] = 'src/' + libvmime_dist_files[i] + +for p in libvmime_messaging_proto_sources: + for f in p[1]: + libvmime_dist_files.append('src/' + f) + +libvmime_dist_files = libvmime_dist_files + libvmime_extra + libvmime_examples_sources +libvmime_dist_files_with_tests = libvmime_dist_files + libvmime_tests + + +################# +# Set options # +################# + +EnsureSConsVersion(0, 94) + +SetOption('implicit_cache', 1) + +#SourceSignatures('timestamp') +SourceSignatures('MD5') +TargetSignatures('build') + + +############# +# Version # +############# + +def GetPackageVersion(): + import re + contents = open('VERSION', 'r').read() + major = re.compile('(\d+)\.(\d+)\.(\d+)', re.DOTALL).sub(r'\1', contents) + minor = re.compile('(\d+)\.(\d+)\.(\d+)', re.DOTALL).sub(r'\2', contents) + patch = re.compile('(\d+)\.(\d+)\.(\d+)', re.DOTALL).sub(r'\3', contents) + return '%d.%d.%d' % (int(major), int(minor), int(patch)) + +packageName = 'libvmime' +packageVersion = GetPackageVersion() + + +############# +# Options # +############# + +# Try to guess some default values +# TODO + +# Command line options +opts = Options('options.cache') + +opts.AddOptions( + ( + 'prefix', + 'Installation prefix directory', + '/usr' + ), + EnumOption( + 'debug', + 'Debug version (useful for developers only)', + 'no', + allowed_values = ('yes', 'no'), + map = { }, + ignorecase = 1 + ), + EnumOption( + 'static', + 'Build a static library (.a)', + 'yes', + allowed_values = ('yes', 'no'), + map = { }, + ignorecase = 1 + ), + EnumOption( + 'shared', + 'Build a shared library (.so)', + 'no', + allowed_values = ('yes', 'no'), + map = { }, + ignorecase = 1 + ), + EnumOption( + 'with_messaging', + 'Messaging support (connection to mail store/transport servers)', + 'yes', + allowed_values = ('yes', 'no'), + map = { }, + ignorecase = 1 + ), + EnumOption( + 'with_filesystem', + 'Enable file-system support (eg. "maildir" messaging support)', + 'yes', + allowed_values = ('yes', 'no'), + map = { }, + ignorecase = 1 + ), + ( + 'with_messaging_protocols', + 'Specifies which protocols to build into the library.\n' + + 'This option has no effect if "with_messaging" is not activated.\n' + + 'Separate protocols with spaces; string must be quoted with ".', + '"pop3 smtp imap"' + ), + EnumOption( + 'with_wide_char_support', + 'Support for wide characters (rarely used, should be set to "no")', + 'no', + allowed_values = ('yes', 'no'), + map = { }, + ignorecase = 1 + ), + EnumOption( + 'byte_order', + 'Byte order', + sys.byteorder, + allowed_values = ('big', 'little'), + map = { }, + ignorecase = 1 + ), + EnumOption( + 'pf_8bit_type', + 'The C-language 8-bit type for your platform', + 'char', + allowed_values = ('char', 'short', 'int', 'long'), + map = { }, + ignorecase = 1 + ), + EnumOption( + 'pf_16bit_type', + 'The C-language 16-bit type for your platform', + 'short', + allowed_values = ('char', 'short', 'int', 'long'), + map = { }, + ignorecase = 1 + ), + EnumOption( + 'pf_32bit_type', + 'The C-language 32-bit type for your platform', + 'int', + allowed_values = ('char', 'short', 'int', 'long'), + map = { }, + ignorecase = 1 + ) +) + + +############################### +# Configuration Environment # +############################### + +env = Environment(options = opts) + +env.Append(ENV = {'PATH' : os.environ['PATH']}) + +env.Append(CPPPATH = [ '.', 'src' ]) + +env.Append(CPPDEFINES = { '_REENTRANT' : 1 }) + +env.Append(CXXFLAGS = ['-pipe']) +env.Append(CXXFLAGS = ['-W']) +env.Append(CXXFLAGS = ['-Wall']) +env.Append(CXXFLAGS = ['-ansi']) +env.Append(CXXFLAGS = ['-pedantic']) + +env.Append(TARFLAGS = ['-c']) +env.Append(TARFLAGS = ['--bzip2']) + +if env['debug'] == 'yes': + env.Append(CXXFLAGS = ['-g']) + env.Append(CXXFLAGS = ['-O0']) +else: + env.Append(CXXFLAGS = ['-O2']) + #-fomit-frame-pointer -fmove-all-movables -fstrict-aliasing ') + +#env.Append(LIBS = ['additional-lib-here']) + +# Generate help text for command line options +Help(opts.GenerateHelpText(env)) + +# Cache current options +opts.Save('options.cache', env) + + +########################## +# Some initializations # +########################## + +# Messaging protocols +messaging_protocols = [ ] + +for proto in re.split('\W+', env['with_messaging_protocols']): + proto = string.strip(proto) + if len(proto) >= 1: + messaging_protocols.append(proto) + +# Show configuration summary +print "" +print "+=================+" +print "| CONFIGURATION |" +print "+=================+" +print "" +print "Installation prefix : " + env['prefix'] +print "Static library (.a) : " + env['static'] +print "Shared library (.so) : " + env['shared'] +print "Debugging mode : " + env['debug'] +print "Messaging support : " + env['with_messaging'] +if env['with_messaging'] == 'yes': + print " * protocols : " + env['with_messaging_protocols'] +print "File-system support : " + env['with_filesystem'] +print "" + + +######################## +# Some sanity checks # +######################## + +def IsProtocolSupported(protoList, proto): + for supportedProto in protoList: + if string.upper(supportedProto) == string.upper(proto): + return 1 + return 0 + +# File-system support must be activated when 'maildir' protocol is selected +if env['with_messaging'] == 'yes': + if IsProtocolSupported(messaging_protocols, 'maildir'): + if env['with_filesystem'] != 'yes': + print "ERROR: 'maildir' protocol requires file-system support!\n" + Exit(1) + + +######################### +# Generate config.hpp # +######################### + +config_hpp = open('src/config.hpp', 'w') + +config_hpp.write(""" +// +// This file was automatically generated by configuration script. +// + +#ifndef VMIME_CONFIG_HPP_INCLUDED +#define VMIME_CONFIG_HPP_INCLUDED + + +""") + +config_hpp.write('// Name of package\n') +config_hpp.write('#define VMIME_PACKAGE "' + packageName + '"\n') +config_hpp.write('\n') +config_hpp.write('// Version number of package\n') +config_hpp.write('#define VMIME_VERSION "' + packageVersion + '"\n') +config_hpp.write('\n') +config_hpp.write('// Target OS and architecture\n') + +if os.name == 'posix': + config_hpp.write('#define VMIME_TARGET_ARCH "' + commands.getoutput('uname -m') + '"\n') + config_hpp.write('#define VMIME_TARGET_OS "' + commands.getoutput('uname -o') + '"\n') +else: + config_hpp.write('#define VMIME_TARGET_ARCH "" // Unknown\n') + config_hpp.write('#define VMIME_TARGET_OS "' + sys.platform + '/' + os.name + '"\n') + +config_hpp.write('\n') + +if os.name == 'posix': + config_hpp.write('#define VMIME_TARGET_OS_IS_POSIX 1 // POSIX compatible\n') + config_hpp.write('#define VMIME_TARGET_OS_IS_WIN32 0 // Win32\n') +elif os.name == 'win32' or os.name == 'nt': + config_hpp.write('#define VMIME_TARGET_OS_IS_POSIX 0 // POSIX compatible\n') + config_hpp.write('#define VMIME_TARGET_OS_IS_WIN32 1 // Win32\n') +else: + print "ERROR: unsupported system: '" + os.name + "'\n" + Exit(1) + +config_hpp.write('\n') +config_hpp.write('// Set to 1 if debugging should be activated\n') + +if env['debug'] == 'yes': + config_hpp.write('#define VMIME_DEBUG 1\n') +else: + config_hpp.write('#define VMIME_DEBUG 0\n') + +config_hpp.write('\n') +config_hpp.write('// Byte order (set one or the other, but not both!)\n') + +if env['byte_order'] == 'big': + config_hpp.write('#define IMEL_BYTE_ORDER_BIG_ENDIAN 1\n') + config_hpp.write('#define IMEL_BYTE_ORDER_LITTLE_ENDIAN 0\n') +else: + config_hpp.write('#define IMEL_BYTE_ORDER_BIG_ENDIAN 0\n') + config_hpp.write('#define IMEL_BYTE_ORDER_LITTLE_ENDIAN 1\n') + +config_hpp.write('\n') +config_hpp.write('// Generic types\n') +config_hpp.write('// -- 8-bit\n') +config_hpp.write('typedef signed ' + env['pf_8bit_type'] + ' vmime_int8;\n') +config_hpp.write('typedef unsigned ' + env['pf_8bit_type'] + ' vmime_uint8;\n') +config_hpp.write('// -- 16-bit\n') +config_hpp.write('typedef signed ' + env['pf_16bit_type'] + ' vmime_int16;\n') +config_hpp.write('typedef unsigned ' + env['pf_16bit_type'] + ' vmime_uint16;\n') +config_hpp.write('// -- 32-bit\n') +config_hpp.write('typedef signed ' + env['pf_32bit_type'] + ' vmime_int32;\n') +config_hpp.write('typedef unsigned ' + env['pf_32bit_type'] + ' vmime_uint32;\n') +config_hpp.write('\n') + +config_hpp.write('// Options\n') + +config_hpp.write('// -- Wide characters support\n') +if env['with_wide_char_support'] == 'yes': + config_hpp.write('#define VMIME_WIDE_CHAR_SUPPORT 1\n') +else: + config_hpp.write('#define VMIME_WIDE_CHAR_SUPPORT 0\n') + +config_hpp.write('// -- File-system support\n'); +if env['with_filesystem'] == 'yes': + config_hpp.write('#define VMIME_HAVE_FILESYSTEM_FEATURES 1\n') +else: + config_hpp.write('#define VMIME_HAVE_FILESYSTEM_FEATURES 0\n') + +config_hpp.write('// -- Messaging support\n'); +if env['with_messaging'] == 'yes': + config_hpp.write('#define VMIME_HAVE_MESSAGING_FEATURES 1\n') + + config_hpp.write('// -- Built-in messaging protocols\n') + config_hpp.write('#define VMIME_BUILTIN_MESSAGING_PROTOS ' + env['with_messaging_protocols'] + '\n') + + for proto in messaging_protocols: + config_hpp.write('#define VMIME_BUILTIN_MESSAGING_PROTO_' + string.upper(proto) + ' 1\n'); +else: + config_hpp.write('#define VMIME_HAVE_MESSAGING_FEATURES 0\n') + +config_hpp.write(""" + +#endif // VMIME_CONFIG_HPP_INCLUDED +""") + +config_hpp.close() + + +################# +# Build rules # +################# + +# Build directory +if env['debug'] == 'yes': + BuildDir("#build/debug", 'src', duplicate = 0) + buildDirectory = 'build/debug/' +else: + BuildDir("#build/release", 'src', duplicate = 0) + buildDirectory = 'build/release/' + +# Create source files list +libvmime_full_sources = libvmime_sources + +if env['with_messaging'] == 'yes': + # -- Add common files for messaging support + for file in libvmime_messaging_sources: + libvmime_full_sources.append(file) + + # -- Add protocol specific source files + for proto in messaging_protocols: + for protosrc in libvmime_messaging_proto_sources: + if protosrc[0] == proto: + for file in protosrc[1]: + libvmime_full_sources.append(file) + +# Split source files list into two lists: .CPP and .HPP +libvmime_sources_CPP = [ ] +libvmime_sources_HPP = [ ] + +libvmime_install_includes = [ ] + +for file in libvmime_full_sources: + slash = string.find(file, '/') + dir = '' + + if slash != -1: + dir = file[0:slash] + '/' + + if file[-4:] == '.cpp': + libvmime_sources_CPP.append(buildDirectory + file) + else: + libvmime_sources_HPP.append(buildDirectory + file) + libvmime_install_includes.append([dir, buildDirectory + file]) + +# Main program build +if env['debug'] == 'yes': + libVmime = env.StaticLibrary( + target = 'vmime-debug', + source = libvmime_sources_CPP + ) + libVmimeSh = env.SharedLibrary( + target = 'vmime-debug', + source = libvmime_sources_CPP + ) +else: + libVmime = env.StaticLibrary( + target = 'vmime', + source = libvmime_sources_CPP + ) + libVmimeSh = env.SharedLibrary( + target = 'vmime', + source = libvmime_sources_CPP + ) + +if env['static'] == 'yes': Default(libVmime) +if env['shared'] == 'yes': Default(libVmimeSh) + + +######################## +# Installation rules # +######################## + +libDir = "%s/lib" % env['prefix'] +includeDir = "%s/include/vmime" % env['prefix'] + +installPaths = [libDir, includeDir] + +# Library +if env['static'] == 'yes': env.Install(libDir, libVmime) +if env['shared'] == 'yes': env.Install(libDir, libVmimeSh) + +# Header files +for i in range(len(libvmime_install_includes)): + env.Install(includeDir + '/' + libvmime_install_includes[i][0], libvmime_install_includes[i][1]) + +# Configuration header file +env.Install(includeDir, 'src/config.hpp') + +# Provide "install" target (ie. 'scons install') +env.Alias('install', installPaths) + + +##################### +# Packaging rules # +##################### + +# 'tar' is not available under Windows... +if not (os.name == 'win32' or os.name == 'nt'): + packageFile = 'libvmime-' + packageVersion + '.tar.bz2' + packageFileWithTests = 'libvmime-' + packageVersion + '-with-tests.tar.bz2' + + #env.Tar(packageFile, libvmime_dist_files) + #env.Tar(packageFileWithTests, libvmime_dist_files_with_tests) + env.Tar(packageFile, libvmime_dist_files_with_tests) + + #env.Alias('dist', [ packageFile, packageFileWithTests ]) + env.Alias('dist', packageFile) diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..2411653a --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.5.2 \ No newline at end of file diff --git a/examples/README b/examples/README new file mode 100644 index 00000000..b8e11937 --- /dev/null +++ b/examples/README @@ -0,0 +1,9 @@ + +1) Configure, compile and install vmime library + +2) Compile the sample programs with: + $ g++ -o exampleX exampleX.cpp ../libvmime.a + +3) For a more complete documentation, please visit: + http://www.kisli.com/vmime/doc/ + diff --git a/examples/example1.cpp b/examples/example1.cpp new file mode 100644 index 00000000..191a2adf --- /dev/null +++ b/examples/example1.cpp @@ -0,0 +1,85 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +// +// EXAMPLE DESCRIPTION: +// ==================== +// This sample program demonstrate the use of the messageBuilder component +// to build a simple message. +// +// For more information, please visit: +// http://vmime.sourceforge.net/ +// + +#include + +#include "../src/vmime" +#include "common.inc" + + +int main() +{ + std::cout << std::endl; + + // VMime initialization + vmime::platformDependant::setHandler(); + + try + { + vmime::messageBuilder mb; + + // Fill in the basic fields + mb.expeditor() = vmime::mailbox("me@somewhere.com"); + mb.recipients().append(vmime::mailbox("you@elsewhere.com")); + mb.blindCopyRecipients().append(vmime::mailbox("you-bcc@nowhere.com")); + mb.subject() = vmime::text("My first message generated with vmime::messageBuilder"); + + // Message body + mb.textPart().text() = "I'm writing this short text to test message construction " \ + "using the vmime::messageBuilder component."; + + // Construction + vmime::message* msg = mb.construct(); + + // Raw text generation + std::cout << "Generated message:" << std::endl; + std::cout << "==================" << std::endl; + + vmime::outputStreamAdapter out(std::cout); + msg->generate(out); + + // Destruction + delete (msg); + } + // VMime exception + catch (vmime::exception& e) + { + std::cout << "vmime::exception: " << e.what() << std::endl; + throw; + } + // Standard exception + catch (std::exception& e) + { + std::cout << "std::exception: " << e.what() << std::endl; + throw; + } + + std::cout << std::endl; +} + diff --git a/examples/example2.cpp b/examples/example2.cpp new file mode 100644 index 00000000..183b6d02 --- /dev/null +++ b/examples/example2.cpp @@ -0,0 +1,99 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +// +// EXAMPLE DESCRIPTION: +// ==================== +// This sample program demonstrate the use of the messageBuilder component +// to build a simple message with an attachment. +// +// For more information, please visit: +// http://vmime.sourceforge.net/ +// + +#include + +#include "../src/vmime" +#include "common.inc" + + +int main() +{ + std::cout << std::endl; + + // VMime initialization + vmime::platformDependant::setHandler(); + + try + { + vmime::messageBuilder mb; + + // Fill in the basic fields + mb.expeditor() = vmime::mailbox("me@somewhere.com"); + mb.recipients().append(vmime::mailbox("you@elsewhere.com")); + mb.blindCopyRecipients().append(vmime::mailbox("you-bcc@nowhere.com")); + mb.subject() = vmime::text("My first message generated with vmime::messageBuilder"); + + // Message body + mb.textPart().text() = "I'm writing this short text to test message construction " \ + "with attachment, using the vmime::messageBuilder component."; + + // Adding an attachment + vmime::fileAttachment* a = new vmime::fileAttachment + ( + "./example2.cpp", // full path to file + vmime::mediaType("application/octet-stream"), // content type + vmime::text("My first attachment") // description + ); + + a->fileInfo().setFilename("example2.cpp"); + a->fileInfo().setCreationDate(vmime::datetime("30 Apr 2003 14:30:00 +0200")); + + mb.attach(a); + + // Construction + vmime::message* msg = mb.construct(); + + // Raw text generation + vmime::string dataToSend = msg->generate(); + + std::cout << "Generated message:" << std::endl; + std::cout << "==================" << std::endl; + std::cout << std::endl; + std::cout << dataToSend << std::endl; + + // Destruction + delete (msg); + } + // VMime exception + catch (vmime::exception& e) + { + std::cout << "vmime::exception: " << e.what() << std::endl; + throw; + } + // Standard exception + catch (std::exception& e) + { + std::cout << "std::exception: " << e.what() << std::endl; + throw; + } + + std::cout << std::endl; +} + diff --git a/examples/example3.cpp b/examples/example3.cpp new file mode 100644 index 00000000..c8cc0f2f --- /dev/null +++ b/examples/example3.cpp @@ -0,0 +1,99 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +// +// EXAMPLE DESCRIPTION: +// ==================== +// This sample program demonstrate the use of the messageBuilder component +// to build a complex message (HTML content, plain text and embedded image). +// +// For more information, please visit: +// http://vmime.sourceforge.net/ +// + +#include + +#include "../src/vmime" +#include "common.inc" + + +int main() +{ + std::cout << std::endl; + + // VMime initialization + vmime::platformDependant::setHandler(); + + try + { + vmime::messageBuilder mb; + + // Fill in the basic fields + mb.expeditor() = vmime::mailbox("me@somewhere.com"); + mb.recipients().append(vmime::mailbox("you@elsewhere.com")); + mb.blindCopyRecipients().append(vmime::mailbox("you-bcc@nowhere.com")); + mb.subject() = vmime::text("My first message generated with vmime::messageBuilder"); + + // Set the content-type to "text/html" + mb.constructTextPart(vmime::mediaType + (vmime::mediaTypes::TEXT, vmime::mediaTypes::TEXT_HTML)); + + // Fill in the text part: the message is available in two formats: HTML and plain text. + // HTML text part also includes an inline image (embedded into the message). + vmime::htmlTextPart& textPart = dynamic_cast(mb.textPart()); + + // -- embed an image (the returned "CID" (content identifier) is used to reference + // -- the image into HTML content). + vmime::string cid = textPart.embeddedObjects.add("<...IMAGE DATA...>", + vmime::mediaType(vmime::mediaTypes::IMAGE, vmime::mediaTypes::IMAGE_JPEG)); + + // -- message text + textPart.text() = vmime::string("This is the HTML text.
"); + textPart.plainText() = vmime::string("This is the plain text (without HTML formatting)."); + + // Construction + vmime::message* msg = mb.construct(); + + // Raw text generation + vmime::string dataToSend = msg->generate(); + + std::cout << "Generated message:" << std::endl; + std::cout << "==================" << std::endl; + std::cout << std::endl; + std::cout << dataToSend << std::endl; + + // Destruction + delete (msg); + } + // VMime exception + catch (vmime::exception& e) + { + std::cout << "vmime::exception: " << e.what() << std::endl; + throw; + } + // Standard exception + catch (std::exception& e) + { + std::cout << "std::exception: " << e.what() << std::endl; + throw; + } + + std::cout << std::endl; +} + diff --git a/examples/example4.cpp b/examples/example4.cpp new file mode 100644 index 00000000..164e4be9 --- /dev/null +++ b/examples/example4.cpp @@ -0,0 +1,94 @@ +// +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +// +// EXAMPLE DESCRIPTION: +// ==================== +// This sample program demonstrate the use of the messageParser component +// to enumerate the text parts in a message. +// +// For more information, please visit: +// http://vmime.sourceforge.net/ +// + +#include + +#include "../src/vmime" +#include "common.inc" + + +int main() +{ + std::cout << std::endl; + + // VMime initialization + vmime::platformDependant::setHandler(); + + try + { + vmime::messageParser mp("<...MIME message content...>"); + + // Enumerate text parts + for (std::vector ::const_iterator i = mp.textParts().begin() ; + i != mp.textParts().end() ; ++i) + { + const vmime::textPart& part = **i; + + // Output content-type of the part + std::cout << part.type().generate() << std::endl; + + // text/html + if (part.type().subType() == vmime::mediaTypes::TEXT_HTML) + { + const vmime::htmlTextPart& hp = dynamic_cast(part); + + // HTML text is in "hp.text()" + // Corresponding plain text is in "hp.plainText()" + + // Enumerate embedded objects (eg. images) + for (vmime::htmlTextPart::const_iterator i = hp.embeddedObjects.begin() ; + i != hp.embeddedObjects.end() ; ++i) + { + // Identifier (content-id or content-location) is in "(*i).id()" + // Object data is in "(*i).data()" + } + } + // text/plain + else + { + const vmime::textPart& tp = dynamic_cast(part); + + // Text is in "tp.text()" + } + } + } + // VMime exception + catch (vmime::exception& e) + { + std::cout << "vmime::exception: " << e.what() << std::endl; + throw; + } + // Standard exception + catch (std::exception& e) + { + std::cout << "std::exception: " << e.what() << std::endl; + throw; + } + + std::cout << std::endl; +} diff --git a/examples/example5.cpp b/examples/example5.cpp new file mode 100644 index 00000000..a36d8c09 --- /dev/null +++ b/examples/example5.cpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +// +// EXAMPLE DESCRIPTION: +// ==================== +// This sample program demonstrate the use of the messageParser component +// to enumerate the attachments in a message. +// +// For more information, please visit: +// http://vmime.sourceforge.net/ +// + +#include + +#include "../src/vmime" +#include "common.inc" + + +int main() +{ + std::cout << std::endl; + + // VMime initialization + vmime::platformDependant::setHandler(); + + try + { + vmime::messageParser mp("<...MIME message content...>"); + + // Enumerate attachments + for (std::vector ::const_iterator i = mp.attachments().begin() ; + i != mp.attachments().end() ; ++i) + { + // Media type (content type) is in "(*i).type()" + // Description is in "(*i).description()" + // Data is in "(*i).data()" + } + } + // VMime exception + catch (vmime::exception& e) + { + std::cout << "vmime::exception: " << e.what() << std::endl; + throw; + } + // Standard exception + catch (std::exception& e) + { + std::cout << "std::exception: " << e.what() << std::endl; + throw; + } + + std::cout << std::endl; +} diff --git a/examples/example6.cpp b/examples/example6.cpp new file mode 100644 index 00000000..6bea365e --- /dev/null +++ b/examples/example6.cpp @@ -0,0 +1,352 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include + +#include "../src/vmime" +#include "common.inc" + + +// +// Authentification handler +// + +class my_auth : public vmime::messaging::authenticator +{ + const vmime::messaging::authenticationInfos requestAuthInfos() const + { + vmime::string username, password; + + std::cout << "Username: "; std::cout.flush(); + std::cin >> username; + + std::cout << "Password: "; std::cout.flush(); + std::cin >> password; + + return (vmime::messaging::authenticationInfos(username, password)); + } +}; + + + + + +void printStructure(const vmime::messaging::structure& s, int level = 0) +{ + for (int i = 1 ; i <= s.count() ; ++i) + { + const vmime::messaging::part& part = s[i]; + + for (int j = 0 ; j < level * 2 ; ++j) + std::cout << " "; + + std::cout << part.number() << ". " + << part.type().generate() + << " [" << part.size() << " byte(s)]" + << std::endl; + + printStructure(part.structure(), level + 1); + } +} + + + +int main() +{ + // VMime initialization + vmime::platformDependant::setHandler (); + + // + // Test the new enumeration system for encoders + // + +#if 0 + vmime::encoderFactory* ef = vmime::encoderFactory::getInstance(); + + std::cout << "Available encoders:" << std::endl; + + for (vmime::encoderFactory::iterator it = ef->begin() ; + it != ef->end() ; ++it) + { + std::cout << " * " << (*it).name() << std::endl; + + vmime::encoder* e = (*it).create(); + + std::vector props = e->availableProperties(); + + for (std::vector ::const_iterator it2 = props.begin() ; it2 != props.end() ; ++it2) + std::cout << " - " << *it2 << std::endl; + + delete (e); + } +#endif + + // ====================================================================================== + + // + // Test the new enumeration system for messaging services + // + +#if 1 + vmime::messaging::serviceFactory* sf = vmime::messaging::serviceFactory::getInstance(); + + std::cout << "Available messaging services:" << std::endl; + + for (vmime::messaging::serviceFactory::const_iterator it = sf->begin() ; + it != sf->end() ; ++it) + { + std::cout << " * " << (*it).name() << " (" << (*it).infos().defaultPort() << ")" << std::endl; + + std::vector props = (*it).infos().availableProperties(); + + for (std::vector ::const_iterator it2 = props.begin() ; it2 != props.end() ; ++it2) + std::cout << " - " << (*it).infos().propertyPrefix() + *it2 << std::endl; + } +#endif + + vmime::messaging::session sess; + sess.properties()["store.protocol"] = "imap"; + sess.properties()["transport.protocol"] = "smtp"; + + my_auth auth; + + try + { + // + // Test the sending of a message + // + +#if 0 + // Transport protocol configuration + vmime::messaging::transport* tr = sess.getTransport(); + + //sess.properties()[tr->infos().propertyPrefix() + "auth.username"] = "username"; + //sess.properties()[tr->infos().propertyPrefix() + "auth.password"] = "password"; + + sess.properties()[tr->infos().propertyPrefix() + "server.address"] = "smtp.mydomain.com"; + + //sess.properties()[tr->infos().propertyPrefix() + "options.need-authentification"] = true; + + // Connection + tr->connect(); + + // Expeditor + vmime::mailbox from("me@somewhere.com"); + + // Recipients list + vmime::mailboxList to; + to.append(vmime::mailbox("you@somewhere.com")); + to.append(vmime::mailbox("somebody.else@anywhere.com")); + + std::istringstream iss("[MESSAGE DATA: HEADER + BODY]"); + tr->send(from, to, iss); + + // Note: you could also write this: + // vmime::message msg; + // ... + // tr->send(&msg); + + tr->disconnect(); +#endif + + // + // Test the access to a mail store + // + +#if 1 + // If no authenticator is given in argument to getStore(), a default one + // is used. Its behaviour is to get the user credentials from the + // session properties "auth.username" and "auth.password". + vmime::messaging::store* st = sess.getStore(&auth); + + // Store protocol configuration + //sess.properties()[st->infos().propertyPrefix() + "auth.username"] = "username"; + //sess.properties()[st->infos().propertyPrefix() + "auth.password"] = "password"; + + sess.properties()[st->infos().propertyPrefix() + "server.address"] = "imap.mydomain.com"; + //sess.properties()[st->infos().propertyPrefix() + "server.port"] = 110; + //sess.properties()[st->infos().propertyPrefix() + "server.socket-factory"] = "default"; + + //sess.properties()[st->infos().propertyPrefix() + "options.apop"] = false; + //sess.properties()[st->infos().propertyPrefix() + "options.apop.fallback"] = true; + + // Connection + st->connect(); + + // Open the default folder in this store + vmime::messaging::folder* f = st->getDefaultFolder(); + + f->open(vmime::messaging::folder::MODE_READ_WRITE); + + std::cout << f->getMessageCount() << " message(s) in your inbox" << std::endl; + + // Get a pointer to the first message + vmime::messaging::message* m = f->getMessage(1); + + // To fetch the header + f->fetchMessage(m, vmime::messaging::folder::FETCH_ENVELOPE | + vmime::messaging::folder::FETCH_CONTENT_INFO); + + // To retrieve the whole message + std::ostringstream oss; + vmime::outputStreamAdapter out(oss); + + m->extract(out); + + // To fetch the header + f->fetchMessage(m, vmime::messaging::folder::FETCH_ENVELOPE | + vmime::messaging::folder::FETCH_CONTENT_INFO | + vmime::messaging::folder::FETCH_STRUCTURE | + vmime::messaging::folder::FETCH_SIZE | + //vmime::messaging::folder::FETCH_FULL_HEADER | + vmime::messaging::folder::FETCH_SIZE | + vmime::messaging::folder::FETCH_FLAGS | + vmime::messaging::folder::FETCH_UID); + + // Print structure + std::cout << "STRUCTURE:" << std::endl; + std::cout << "==========" << std::endl; + + printStructure(m->structure()); + + std::cout << std::endl; + + std::cout << "Size = " << m->size() << " byte(s)" << std::endl; + std::cout << "UID = " << m->uniqueId() << std::endl; + std::cout << std::endl; + + std::cout << "ENVELOPE:" << std::endl; + std::cout << "=========" << std::endl; + try { std::cout << m->header().fields.From().generate() << std::endl; } catch (...) { } + try { std::cout << m->header().fields.To().generate() << std::endl; } catch (...) { } + try { std::cout << m->header().fields.Date().generate() << std::endl; } catch (...) { } + try { std::cout << m->header().fields.Subject().generate() << std::endl; } catch (...) { } + + std::cout << std::endl; + + std::cout << "FULL HEADER:" << std::endl; + std::cout << "============" << std::endl; + std::cout << m->header().generate() << std::endl; + + std::cout << std::endl; + std::cout << "=========================================================" << std::endl; + + vmime::outputStreamAdapter out2(std::cout); + m->extractPart(m->structure()[1][2][1], out2, NULL); //, 0, 10); + + std::cout << "=========================================================" << std::endl; + + std::cout << std::endl; + std::cout << "=========================================================" << std::endl; + + m->fetchPartHeader(m->structure()[1][2][1]); + + std::cout << m->structure()[1][2][1].header().generate() << std::endl; + + std::cout << "=========================================================" << std::endl; + + // Flags manipulation + std::cout << "Flags = " << m->flags() << std::endl; + m->setFlags(vmime::messaging::message::FLAG_REPLIED, vmime::messaging::message::FLAG_MODE_ADD); + std::cout << "Flags = " << m->flags() << std::endl; + m->setFlags(vmime::messaging::message::FLAG_REPLIED, vmime::messaging::message::FLAG_MODE_REMOVE); + std::cout << "Flags = " << m->flags() << std::endl; + + f->setMessageFlags(m->number(), m->number(), vmime::messaging::message::FLAG_REPLIED, vmime::messaging::message::FLAG_MODE_ADD); + std::cout << "Flags = " << m->flags() << std::endl; + f->setMessageFlags(m->number(), m->number(), vmime::messaging::message::FLAG_REPLIED, vmime::messaging::message::FLAG_MODE_REMOVE); + std::cout << "Flags = " << m->flags() << std::endl; + + + std::cout << "=========================================================" << std::endl; + + // Append message +/* + std::istringstream iss( + "From: me@localhost\r\n" + "To: you@localhost\r\n" + "Subject: Message Text\r\n" + "\r\n" + "This is a test message...\r\n" + "Bye bye!\r\n" + ); + + f->addMessage(iss, iss.str().size()); +*/ + + +/* + // Folder renaming + { + vmime::messaging::folder* f = st->getFolder(vmime::messaging::folder::path("c")); + f->rename(vmime::messaging::folder::path("c2")); + delete (f); + + vmime::messaging::folder* g = st->getFolder(vmime::messaging::folder::path("c2")); + g->rename(vmime::messaging::folder::path("c")); + delete (g); + } +*/ + +/* + // Message copy + { + vmime::messaging::folder* g = st->getFolder(vmime::messaging::folder::path("TEMP")); + + if (!g->exists()) + g->create(vmime::messaging::folder::TYPE_CONTAINS_MESSAGES); + + f->copyMessages(g->fullPath()); + + delete (g); + } +*/ + + delete (m); + + f->close(true); + delete (f); + + st->disconnect(); + delete (st); +#endif + } + catch (vmime::exceptions::authentication_error& e) + { + std::cout << "vmime::authentication_error: " << e.what() << std::endl + << "Response is: '" << e.response() << "'." << std::endl; + throw; + } + catch (vmime::exceptions::command_error& e) + { + std::cout << "vmime::command_error: " << e.what() << std::endl + << "Response is: '" << e.response() << "'." << std::endl; + throw; + } + catch (vmime::exception& e) + { + std::cout << "vmime::exception: " << e.what() << std::endl; + throw; + } + catch (std::exception& e) + { + std::cout << "std::exception: " << e.what() << std::endl; + throw; + } +} diff --git a/src/address.cpp b/src/address.cpp new file mode 100644 index 00000000..481cc209 --- /dev/null +++ b/src/address.cpp @@ -0,0 +1,201 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "address.hpp" + +#include "mailbox.hpp" +#include "mailboxGroup.hpp" + +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +address::address() +{ +} + + +/* + + RFC #2822: + 3.4. ADDRESS SPECIFICATION + + Addresses occur in several message header fields to indicate senders + and recipients of messages. An address may either be an individual + mailbox, or a group of mailboxes. + +address = mailbox / group + +mailbox = name-addr / addr-spec + +name-addr = [display-name] angle-addr + +angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr + +group = display-name ":" [mailbox-list / CFWS] ";" + [CFWS] + +display-name = phrase + +mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list + +address-list = (address *("," address)) / obs-addr-list + +*/ + +address* address::parseNext(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + bool escaped = false; + bool quoted = false; + bool quotedRFC2047 = false; + bool inRouteAddr = false; + bool isGroup = false; + bool stop = false; + + string::size_type pos = position; + + while (pos < end && isspace(buffer[pos])) + ++pos; + + const string::size_type start = pos; + + while (!stop && pos < end) + { + if (escaped) + { + escaped = false; + } + else + { + switch (buffer[pos]) + { + case '\\': + escaped = true; + break; + case '"': + quoted = !quoted; + break; + case '<': + inRouteAddr = true; + break; + case '>': + inRouteAddr = false; + break; + case '=': + + if (pos + 1 < end && buffer[pos + 1] == '?') + { + ++pos; + quotedRFC2047 = true; + } + + break; + + case '?': + + if (quotedRFC2047 && pos + 1 < end && buffer[pos + 1] == '=') + { + ++pos; + quotedRFC2047 = false; + } + + break; + + default: + { + if (!quoted && !quotedRFC2047 && !inRouteAddr) + { + switch (buffer[pos]) + { + case ';': + + if (isGroup) + { + if (pos + 1 < end && buffer[pos + 1] == ',') + ++pos; + } + + stop = true; + break; + + case ':': + + isGroup = true; + break; + + case ',': + + if (!isGroup) stop = true; + break; + } + } + + break; + } + + } + } + + if (!stop) + ++pos; + } + + if (newPosition) + { + if (pos == end) + *newPosition = end; + else + *newPosition = pos + 1; // ',' or ';' + } + + // Parse extracted address (mailbox or group) + if (pos != start) + { + address* parsedAddress = isGroup + ? static_cast(new mailboxGroup) + : static_cast(new mailbox); + + try + { + parsedAddress->parse(buffer, start, pos, NULL); + return (parsedAddress); + } + catch (std::exception&) + { + delete (parsedAddress); + throw; + } + } + + return (NULL); +} + + +address& address::operator=(const address& addr) +{ + copyFrom(addr); + return (*this); +} + + +} // vmime diff --git a/src/address.hpp b/src/address.hpp new file mode 100644 index 00000000..a3d8cedb --- /dev/null +++ b/src/address.hpp @@ -0,0 +1,99 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ADDRESS_HPP_INCLUDED +#define VMIME_ADDRESS_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + + +namespace vmime +{ + + +/** Abstract class representing a mailbox or a group of mailboxes. + * + * This class define a common behaviour for the mailbox + * and mailboxGroup classes. + */ + +class address : public component +{ + friend class addressList; + +protected: + + address(); + +public: + + /** Copy data from another object to this object. + * Both objects must be the same type. + * + * @param addr other object + */ + address& operator=(const address& addr); + + /** Duplicate this object. + * + * @return a copy of this object + */ + virtual address* clone() const = 0; + + /** Copy data from another object to this object. + * Both objects must be the same type. + * + * @param addr other object + */ + virtual void copyFrom(const address& addr) = 0; + + /** Check whether this address is empty (no mailboxes specified + * if this is a mailboxGroup -or- no email specified if this is + * a mailbox). + * + * @return true if this address is empty + */ + virtual const bool empty() const = 0; + + /** Test whether this is object is a mailboxGroup. + * + * @return true if this is a mailboxGroup, false otherwise + */ + virtual const bool isGroup() const = 0; + +protected: + + /** Parse an address from an input buffer. + * + * @param buffer input buffer + * @param position position in the input buffer + * @param end end position in the input buffer + * @param newPosition will receive the new position in the input buffer + * @return a new address object, or null if no more address is available in the input buffer + */ + static address* parseNext(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition); +}; + + +} // vmime + + +#endif // VMIME_ADDRESS_HPP_INCLUDED diff --git a/src/addressList.cpp b/src/addressList.cpp new file mode 100644 index 00000000..c532c16d --- /dev/null +++ b/src/addressList.cpp @@ -0,0 +1,188 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "addressList.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +addressList::addressList() +{ +} + + +addressList::addressList(const class addressList& addressList) + : component() +{ + copyFrom(addressList); +} + + +addressList::~addressList() +{ + clear(); +} + + +void addressList::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + clear(); + + string::size_type pos = position; + + while (pos < end) + { + address* parsedAddress = address::parseNext(buffer, pos, end, &pos); + + if (parsedAddress != NULL) + m_list.push_back(parsedAddress); + } + + if (newPosition) + *newPosition = end; +} + + +void addressList::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + if (!m_list.empty()) + { + string::size_type pos = curLinePos; + const_iterator i = m_list.begin(); + + for ( ; ; ) + { + (*i).generate(os, maxLineLength - 2, pos, &pos); + + if (++i != m_list.end()) + { + os << ", "; + pos += 2; + } + else + { + break; + } + } + + if (newLinePos) + *newLinePos = pos; + } +} + + +/** Return the number of addresses in the list. + * + * @return number of addresses in the list + */ + +const std::vector ::size_type addressList::size() const +{ + return (m_list.size()); +} + + +/** Return the number of addresses in the list. + * + * @return number of addresses in the list + */ + +const std::vector ::size_type addressList::count() const +{ + return (m_list.size()); +} + + +/** Test whether the list is empty. + * + * @return true if the list is empty, false otherwise + */ + +const bool addressList::empty() const +{ + return (m_list.empty()); +} + + +/** Append an address to the list. + * + * @param addr the address to add + */ + +void addressList::append(const address& addr) +{ + m_list.push_back(addr.clone()); +} + + +/** Insert an address at the specified position in the list. + * + * @param it position of the new address + * @param addr the address to insert + */ + +void addressList::insert(const iterator it, const address& addr) +{ + m_list.insert(it.m_iterator, addr.clone()); +} + + +/** Remove the address at the specified position. + * + * @param it position of the address to remove + */ + +void addressList::erase(const iterator it) +{ + delete (*it.m_iterator); + m_list.erase(it.m_iterator); +} + + +/** Remove all the addresses from the list. + */ + +void addressList::clear() +{ + free_container(m_list); +} + + +void addressList::copyFrom(const addressList& source) +{ + clear(); + + for (std::vector ::const_iterator i = source.m_list.begin() ; i != source.m_list.end() ; ++i) + m_list.push_back((*i)->clone()); +} + + +addressList& addressList::operator=(const addressList& source) +{ + copyFrom(source); + return (*this); +} + + +} // vmime diff --git a/src/addressList.hpp b/src/addressList.hpp new file mode 100644 index 00000000..03a53e57 --- /dev/null +++ b/src/addressList.hpp @@ -0,0 +1,148 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ADDRESSLIST_HPP_INCLUDED +#define VMIME_ADDRESSLIST_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "address.hpp" + + +namespace vmime +{ + + +/** A list of addresses. + */ + +class addressList : public component +{ + friend class addressListField; + friend class mailboxListField; + +public: + + addressList(); + addressList(const class addressList& addressList); + + ~addressList(); + +public: + + addressList& operator=(const addressList& source); + + // Address iterator + class const_iterator; + + class iterator + { + friend class addressList; + friend class const_iterator; + + public: + + iterator(std::vector ::iterator it) : m_iterator(it) { } + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + address& operator*() const { return (**m_iterator); } + address* operator->() const { return (*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator& operator++(int) { ++m_iterator; return (*this); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + private: + + std::vector ::iterator m_iterator; + }; + + class const_iterator + { + friend class addressList; + + public: + + const_iterator(std::vector ::const_iterator it) : m_iterator(it) { } + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const address& operator*() const { return (**m_iterator); } + const address* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator& operator++(int) { ++m_iterator; return (*this); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + private: + + std::vector ::const_iterator m_iterator; + }; + + iterator begin() { return (m_list.begin()); } + iterator end() { return (m_list.end()); } + + const_iterator begin() const { return (const_iterator(m_list.begin())); } + const_iterator end() const { return (const_iterator(m_list.end())); } + + const std::vector ::size_type size() const; + const std::vector ::size_type count() const; + const bool empty() const; + + const address& operator[](const std::vector ::size_type x) const { return (*m_list[x]); } + address& operator[](const std::vector ::size_type x) { return (*m_list[x]); } + + virtual void append(const address& addr); + virtual void insert(const iterator it, const address& addr); + + void erase(const iterator it); + void clear(); + +protected: + + std::vector m_list; + + void copyFrom(const addressList& source); + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_ADDRESSLIST_HPP_INCLUDED diff --git a/src/addressListField.cpp b/src/addressListField.cpp new file mode 100644 index 00000000..020d6b10 --- /dev/null +++ b/src/addressListField.cpp @@ -0,0 +1,67 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "addressListField.hpp" + + +namespace vmime +{ + + +addressListField::addressListField() +{ +} + + +void addressListField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_list.parse(buffer, position, end, newPosition); +} + + +void addressListField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + m_list.generate(os, maxLineLength, pos, newLinePos); +} + + +addressListField& addressListField::operator=(const addressList& list) +{ + m_list.copyFrom(list); + return (*this); +} + + +void addressListField::copyFrom(const headerField& field) +{ + const addressListField& source = dynamic_cast(field); + m_list = source.m_list; + + headerField::copyFrom(field); +} + + +} // vmime + diff --git a/src/addressListField.hpp b/src/addressListField.hpp new file mode 100644 index 00000000..1cc63ff6 --- /dev/null +++ b/src/addressListField.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ADDRESSLISTFIELD_HPP_INCLUDED +#define VMIME_ADDRESSLISTFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "addressList.hpp" + + +namespace vmime +{ + + +class addressListField : public headerField +{ + friend class headerFieldFactory::registerer ; + +protected: + + addressListField(); + +public: + + void copyFrom(const headerField& field); + + addressListField& operator=(const addressList& list); + + const addressList& value() const { return (m_list); } + addressList& value() { return (m_list); } + +protected: + + addressList m_list; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_ADDRESSLISTFIELD_HPP_INCLUDED diff --git a/src/attachment.hpp b/src/attachment.hpp new file mode 100644 index 00000000..083f98d8 --- /dev/null +++ b/src/attachment.hpp @@ -0,0 +1,82 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ATTACHMENT_HPP_INCLUDED +#define VMIME_ATTACHMENT_HPP_INCLUDED + + +#include "base.hpp" + +#include "bodyPart.hpp" +#include "mediaType.hpp" +#include "text.hpp" +#include "contentHandler.hpp" +#include "encoding.hpp" + + +namespace vmime +{ + + +class attachment +{ + friend class messageBuilder; + friend class messageParser; + +protected: + + attachment() { } + +public: + + virtual ~attachment() { } + + virtual attachment& operator=(const attachment& attach) = 0; + + /** Return the media type of this attachment. + * @return content type of the attachment + */ + virtual const mediaType& type() const = 0; + + /** Return the description of this attachment. + * @return attachment description + */ + virtual const text& description() const = 0; + + /** Return the data contained in this attachment. + * @return attachment data + */ + virtual const contentHandler& data() const = 0; + + /** Return the encoding used for this attachment. + * @return attachment data encoding + */ + virtual const class encoding& encoding() const = 0; + + /** Generate the attachment in the specified body part. + * @param parent body part in which to generate the attachment + */ + virtual void generateIn(bodyPart& parent) const = 0; +}; + + +} // vmime + + +#endif // VMIME_ATTACHMENT_HPP_INCLUDED diff --git a/src/base.cpp b/src/base.cpp new file mode 100644 index 00000000..84fdc9fc --- /dev/null +++ b/src/base.cpp @@ -0,0 +1,877 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "config.hpp" + +#include "charset.hpp" +#include "base.hpp" + +#include "encoder.hpp" +#include "encoderB64.hpp" +#include "encoderQP.hpp" + +#include "text.hpp" + +#include "parserHelpers.hpp" + +// For initializing +#include "encoderFactory.hpp" +#include "headerFieldFactory.hpp" +#include "parameterFactory.hpp" +#include "textPartFactory.hpp" +#include "options.hpp" + +#if VMIME_HAVE_MESSAGING_FEATURES + #include "messaging/serviceFactory.hpp" +#endif + + +namespace vmime +{ + + +/** "Null" (empty) string. + */ +const string NULL_STRING; + +#if VMIME_WIDE_CHAR_SUPPORT + /** "Null" (empty) wide-char string. + */ + const wstring NULL_WSTRING; +#endif + +/** "Null" (empty) text. + */ +const text NULL_TEXT; + + +/** Return the library name (eg. "libvmime"). + * + * @return library name + */ +const string libname() { return (VMIME_PACKAGE); } + +/** Return the library version (eg. "0.5.2"). + * + * @return library version + */ +const string libversion() { return (VMIME_VERSION " (" __DATE__ " " __TIME__ ")"); } + + +// New line sequence to be used when folding header fields. +const string NEW_LINE_SEQUENCE("\r\n "); +const string::size_type NEW_LINE_SEQUENCE_LENGTH(1); // space + +/** The CR-LF sequence. + */ +const string CRLF("\r\n"); + + +/** The current MIME version supported by VMime. + */ +const string MIME_VERSION("1.0"); + + +// Line length limits +namespace lineLengthLimits +{ + const string::size_type infinite = std::numeric_limits ::max(); +} + + + +/** Test two strings for equality (case insensitive). + * WARNING: use this with ASCII-only strings. + * + * @param s1 first string + * @param s2 second string (must be in lower-case!) + * @param n length of the second string + * @return true if the two strings compare equally, false otherwise + */ + +bool isStringEqualNoCase(const string& s1, const char* s2, const string::size_type n) +{ + // 'n' is the number of characters to compare + // 's2' must be in lowercase letters only + if (s1.length() < n) + return (false); + + bool equal = true; + + for (string::size_type i = 0 ; equal && i < n ; ++i) + equal = (std::tolower(s1[i], std::locale()) == s2[i]); + + return (equal); +} + + +/** Test two strings for equality (case insensitive). + * WARNING: use this with ASCII-only strings. + * + * @param s1 first string + * @param s2 second string + * @return true if the two strings compare equally, false otherwise + */ + +bool isStringEqualNoCase(const string& s1, const string& s2) +{ + if (s1.length() != s2.length()) + return (false); + + bool equal = true; + const string::const_iterator end = s1.end(); + + for (string::const_iterator i = s1.begin(), j = s2.begin(); i != end ; ++i, ++j) + equal = (std::tolower(*i, std::locale()) == std::tolower(*j, std::locale())); + + return (equal); +} + + +/** Test two strings for equality (case insensitive). + * WARNING: use this with ASCII-only strings. + * + * @param begin start position of the first string + * @param end end position of the first string + * @param s second string (must be in lower-case!) + * @param n length of the second string + * @return true if the two strings compare equally, false otherwise + */ + +bool isStringEqualNoCase(const string::const_iterator begin, const string::const_iterator end, + const char* s, const string::size_type n) +{ + if ((string::size_type)(end - begin) < n) + return (false); + + bool equal = true; + char* c = const_cast(s); + string::size_type r = n; + + for (string::const_iterator i = begin ; equal && r && *c ; ++i, ++c, --r) + equal = (std::tolower(*i, std::locale()) == *c); + + return (r == 0 && equal); +} + + +/** Transform all the characters in a string to lower-case. + * WARNING: use this with ASCII-only strings. + * + * @param str the string to transform + * @return a new string in lower-case + */ + +const string toLower(const string& str) +{ + string out(str); + const string::iterator end = out.end(); + + for (string::iterator i = out.begin() ; i != end ; ++i) + *i = std::tolower(*i, std::locale()); + + return (out); +} + + +/** Strip the space characters (SPC, TAB, CR, LF) at the beginning + * and at the end of the specified string. + * + * @param str string in which to strip spaces + * @return a new string with space characters removed + */ + +const string trim(const string& str) +{ + string::const_iterator b = str.begin(); + string::const_iterator e = str.end(); + + if (b != e) + { + for ( ; b != e && isspace(*b) ; ++b); + for ( ; e != b && isspace(*(e - 1)) ; --e); + } + + return (string(b, e)); +} + + +/** Return the number of 7-bit US-ASCII characters in a string. + * + * @param begin start position + * @param end end position + * @return number of ASCII characters + */ + +string::size_type countASCIIchars + (const string::const_iterator begin, const string::const_iterator end) +{ + string::size_type count = 0; + + for (string::const_iterator i = begin ; i != end ; ++i) + { + if (isascii(*i)) + { + if (*i != '=' || *(i + 1) != '?') // To avoid bad behaviour... + ++count; + } + } + + return (count); +} + + +/** Encode and fold text in respect to RFC-2047. + * + * @param os output stream + * @param in input text + * @param maxLineLength maximum line length for output + * @param firstLineOffset the first line length (may be useful if the current output line is not empty) + * @param lastLineLength will receive the length of the last line written + * @param flags encoding flags (see encodeAndFoldFlags) + */ + +void encodeAndFoldText(utility::outputStream& os, const text& in, const string::size_type maxLineLength, + const string::size_type firstLineOffset, string::size_type* lastLineLength, const int flags) +{ + string::size_type curLineLength = firstLineOffset; + + for (text::const_iterator wi = in.begin() ; wi != in.end() ; ++wi) + { + const word& w = *wi; + const string& buffer = w.buffer(); + + // Calculate the number of ASCII chars to check whether encoding is needed + // and _which_ encoding to use. + const string::size_type asciiCount = countASCIIchars(buffer.begin(), buffer.end()); + + bool noEncoding = (flags & encodeAndFoldFlags::forceNoEncoding) || + (!(flags & encodeAndFoldFlags::forceEncoding) && asciiCount == buffer.length()); + + if (noEncoding) + { + // We will fold lines without encoding them. + + string::const_iterator lastWSpos = buffer.end(); // last white-space position + string::const_iterator curLineStart = buffer.begin(); // current line start + + string::const_iterator p = buffer.begin(); + const string::const_iterator end = buffer.end(); + + bool finished = false; + bool newLine = false; + + while (!finished) + { + for ( ; p != end ; ++p, ++curLineLength) + { + // Exceeded maximum line length, but we have found a white-space + // where we can cut the line... + if (curLineLength >= maxLineLength && lastWSpos != end) + break; + + if (*p == ' ' || *p == '\t') + { + // Remember the position of this white-space character + lastWSpos = p; + } + } + + if (p != end) + ++curLineLength; + + //if (p == end || curLineLength >= maxLineLength) + { + if (p == end || lastWSpos == end) + { + // If we are here, it means that we have found no whitespace + // before the first "maxLineLength" characters. In this case, + // we write the full line no matter of the max line length... + + if (!newLine && p != end && lastWSpos == end && + wi != in.begin() && curLineStart == buffer.begin()) + { + // Here, we are continuing on the line of previous encoded + // word, but there is not even enough space to put the + // first word of this line, so we start a new line. + if (flags & encodeAndFoldFlags::noNewLineSequence) + { + os << CRLF; + curLineLength = 0; + } + else + { + os << NEW_LINE_SEQUENCE; + curLineLength = NEW_LINE_SEQUENCE_LENGTH; + } + + p = curLineStart; + lastWSpos = end; + newLine = true; + } + else + { + os << string(curLineStart, p); + + if (p == end) + { + finished = true; + } + else + { + if (flags & encodeAndFoldFlags::noNewLineSequence) + { + os << CRLF; + curLineLength = 0; + } + else + { + os << NEW_LINE_SEQUENCE; + curLineLength = NEW_LINE_SEQUENCE_LENGTH; + } + + curLineStart = p; + lastWSpos = end; + newLine = true; + } + } + } + else + { + // In this case, there will not be enough space on the line for all the + // characters _after_ the last white-space; so we cut the line at this + // last white-space. + +#if 1 + if (curLineLength != 1 && wi != in.begin()) + os << " "; // Separate from previous word +#endif + + os << string(curLineStart, lastWSpos); + + if (flags & encodeAndFoldFlags::noNewLineSequence) + { + os << CRLF; + curLineLength = 0; + } + else + { + os << NEW_LINE_SEQUENCE; + curLineLength = NEW_LINE_SEQUENCE_LENGTH; + } + + curLineStart = lastWSpos + 1; + + p = lastWSpos + 1; + lastWSpos = end; + newLine = true; + } + } + } + } + /* + RFC #2047: + 4. Encodings + + Initially, the legal values for "encoding" are "Q" and "B". These + encodings are described below. The "Q" encoding is recommended for + use when most of the characters to be encoded are in the ASCII + character set; otherwise, the "B" encoding should be used. + Nevertheless, a mail reader which claims to recognize 'encoded-word's + MUST be able to accept either encoding for any character set which it + supports. + */ + else + { + // We will encode _AND_ fold lines + + /* + RFC #2047: + 2. Syntax of encoded-words + + " While there is no limit to the length of a multiple-line header + field, each line of a header field that contains one or more + 'encoded-word's is limited to 76 characters. " + */ + + const string::size_type maxLineLength3 = + (maxLineLength == lineLengthLimits::infinite) + ? maxLineLength + : std::min(maxLineLength, (const string::size_type) 76); + + // Base64 if more than 60% non-ascii, quoted-printable else (default) + const string::size_type asciiPercent = (100 * asciiCount) / buffer.length(); + const string::value_type encoding = (asciiPercent <= 40) ? 'B' : 'Q'; + + string wordStart("=?" + w.charset().name() + "?" + encoding + "?"); + string wordEnd("?="); + + const string::size_type minWordLength = wordStart.length() + wordEnd.length(); + const string::size_type maxLineLength2 = (maxLineLength3 < minWordLength + 1) + ? maxLineLength3 + minWordLength + 1 : maxLineLength3; + + // Checks whether remaining space on this line is usable. If too few + // characters can be encoded, start a new line. + bool startNewLine = true; + + if (curLineLength + 2 < maxLineLength2) + { + const string::size_type remainingSpaceOnLine = maxLineLength2 - curLineLength - 2; + + if (remainingSpaceOnLine < minWordLength + 10) + { + // Space for no more than 10 encoded chars! + // It is not worth while to continue on this line... + startNewLine = true; + } + else + { + // OK, there is enough usable space on the current line. + startNewLine = false; + } + } + + if (startNewLine) + { + os << NEW_LINE_SEQUENCE; + curLineLength = NEW_LINE_SEQUENCE_LENGTH; + } + + // Encode and fold input buffer + string::const_iterator pos = buffer.begin(); + string::size_type remaining = buffer.length(); + + encoder* theEncoder = ((encoding == 'B') + ? ((encoder*) new encoderB64) + : ((encoder*) new encoderQP)); + + string qpEncodedBuffer; + + if (encoding == 'Q') + { + theEncoder->properties()["rfc2047"] = true; + + // In the case of Quoted-Printable encoding, we cannot simply encode input + // buffer line by line. So, we encode the whole buffer and we will fold it + // in the next loop... + utility::inputStreamStringAdapter in(buffer); + utility::outputStreamStringAdapter out(qpEncodedBuffer); + + theEncoder->encode(in, out); + + pos = qpEncodedBuffer.begin(); + remaining = qpEncodedBuffer.length(); + } + +#if 1 + if (curLineLength != 1 && wi != in.begin()) + { + os << " "; // Separate from previous word + ++curLineLength; + } +#endif + + for ( ; remaining ; ) + { + // Start a new encoded word + os << wordStart; + curLineLength += minWordLength; + + // Compute the number of encoded chars that will fit on this line + const string::size_type fit = maxLineLength2 - curLineLength; + + // Base-64 encoding + if (encoding == 'B') + { + // TODO: WARNING! "Any encoded word which encodes a non-integral + // number of characters or octets is incorrectly formed." + + // Here, we have a formula to compute the maximum number of source + // characters to encode knowing the maximum number of encoded chars + // (with Base64, 3 bytes of input provide 4 bytes of output). + string::size_type count = (fit > 1) ? ((fit - 1) * 3) / 4 : 1; + if (count > remaining) count = remaining; + + utility::inputStreamStringAdapter in + (buffer, pos - buffer.begin(), pos - buffer.begin() + count); + + curLineLength += theEncoder->encode(in, os); + + pos += count; + remaining -= count; + } + // Quoted-Printable encoding + else + { + // TODO: WARNING! "Any encoded word which encodes a non-integral + // number of characters or octets is incorrectly formed." + + // All we have to do here is to take a certain number of character + // (that is less than or equal to "fit") from the QP encoded buffer, + // but we also make sure not to fold a "=XY" encoded char. + const string::const_iterator qpEnd = qpEncodedBuffer.end(); + string::const_iterator lastFoldPos = pos; + string::const_iterator p = pos; + string::size_type n = 0; + + while (n < fit && p != qpEnd) + { + if (*p == '=') + { + if (n + 3 >= fit) + { + lastFoldPos = p; + break; + } + + p += 3; + n += 3; + } + else + { + ++p; + ++n; + } + } + + if (lastFoldPos == pos) + lastFoldPos = p; + + os << string(pos, lastFoldPos); + + curLineLength += (lastFoldPos - pos) + 1; + + pos += n; + remaining -= n; + } + + // End of the encoded word + os << wordEnd; + + if (remaining) + { + os << NEW_LINE_SEQUENCE; + curLineLength = NEW_LINE_SEQUENCE_LENGTH; + } + } + + delete (theEncoder); + } + } + + if (lastLineLength) + *lastLineLength = curLineLength; +} + + +void decodeAndUnfoldText(const string::const_iterator& inStart, const string::const_iterator& inEnd, text& out) +{ + // NOTE: See RFC-2047, Pages 11-12 for knowing about handling + // of white-spaces between encoded words. + + out.clear(); + + string::const_iterator p = inStart; + const string::const_iterator end = inEnd; + + const charset defaultCharset(charsets::US_ASCII); + charset prevWordCharset(defaultCharset); + + bool prevIsEncoded = false; + + string::const_iterator prevPos = p; + + for ( ; ; ) + { + if (p == end || *p == '\n') + { + string::const_iterator textEnd = p; + + if (textEnd != inStart && *(textEnd - 1) == '\r') + --textEnd; + + if (textEnd != prevPos) + { + if (out.size() && prevWordCharset == defaultCharset) + { + out.back().buffer() += string(prevPos, textEnd); + } + else + { + prevWordCharset = defaultCharset; + out.append(word(string(prevPos, textEnd), defaultCharset)); + prevIsEncoded = false; + } + } + + if (p == end) + { + // Finished + break; + } + + // Skip the new-line character + prevPos = ++p; + } + else if (*p == '=' && (p + 1) != end && *(p + 1) == '?') + { + string::const_iterator wordPos = p; + p += 2; // skip '=?' + + if (p != end) + { + const string::const_iterator charsetPos = p; + + for ( ; p != end && *p != '?' ; ++p); + + if (p != end) // a charset is specified + { + const string::const_iterator charsetEnd = p; + const string::const_iterator encPos = ++p; // skip '?' + + for ( ; p != end && *p != '?' ; ++p); + + if (p != end) // an encoding is specified + { + //const string::const_iterator encEnd = p; + const string::const_iterator dataPos = ++p; // skip '?' + + for ( ; p != end && !(*p == '?' && *(p + 1) == '=') ; ++p); + + if (p != end) // some data is specified + { + const string::const_iterator dataEnd = p; + p += 2; // skip '?=' + + encoder* theEncoder = NULL; + + // Base-64 encoding + if (*encPos == 'B' || *encPos == 'b') + { + theEncoder = new encoderB64; + } + // Quoted-Printable encoding + else if (*encPos == 'Q' || *encPos == 'q') + { + theEncoder = new encoderQP; + theEncoder->properties()["rfc2047"] = true; + } + + if (theEncoder) + { + // Decode text + string decodedBuffer; + + utility::inputStreamStringAdapter ein(string(dataPos, dataEnd)); + utility::outputStreamStringAdapter eout(decodedBuffer); + + theEncoder->decode(ein, eout); + delete (theEncoder); + + // Append all the unencoded text before this word + if (prevPos != wordPos) + { + string::const_iterator p = prevPos; + + if (prevIsEncoded) + { + // Check whether there are only white-spaces between + // the two encoded words + for ( ; (p != wordPos) && isspace(*p) ; ++p); + } + + if (p != wordPos) // if not empty + { + if (out.size() && prevWordCharset == defaultCharset) + { + out.back().buffer() += string(prevPos, wordPos); + } + else + { + out.append(word(string(prevPos, wordPos), defaultCharset)); + prevWordCharset = defaultCharset; + } + } + } + + // Append this fresh decoded word to output text + charset thisCharset(string(charsetPos, charsetEnd)); + + if (out.size() && prevWordCharset == thisCharset) + { + out.back().buffer() += decodedBuffer; + } + else + { + prevWordCharset = thisCharset; + out.append(word(decodedBuffer, thisCharset)); + } + + // This word has been decoded: we can advance in the input buffer + prevPos = p; + prevIsEncoded = true; + } + else + { + // Unknown encoding: can't decode this word, we will + // treat this word as ordinary text (RFC-2047, Page 9). + } + } + } + } + } + } + else + { + ++p; + } + + for ( ; p != end && *p != '=' && *p != '\n' ; ++p); + } +} + + +void decodeAndUnfoldText(const string& in, text& out) +{ + decodeAndUnfoldText(in.begin(), in.end(), out); +} + + +/** This function can be used to make several encoded words from a text. + * All the characters in the text must be in the same specified charset. + * + *

Eg: giving:

+ *
   <iso-8859-1> "Linux dans un t'el'ephone mobile"
+  *    ("=?iso-8859-1?Q?Linux_dans_un_t=E9l=E9phone_mobile?=")
+  * 

it will return:

+ *
   <:us-ascii>   "Linux dans un "
+  *    <iso-8859-1> "t'el'ephone "
+  *    <us-ascii>   "mobile"
+  *    ("Linux dans un =?iso-8859-1?Q?t=E9l=E9phone_?= mobile")
+  * 
+ * + * @param in input string + * @param ch input charset + * @param out output text + */ + +void makeWordsFromText(const string& in, const charset& ch, text& out) +{ + const string::const_iterator end = in.end(); + string::const_iterator p = in.begin(); + string::const_iterator start = in.begin(); + + bool is8bit = false; // is the current word 8-bit? + bool prevIs8bit = false; // is previous word 8-bit? + unsigned int count = 0; // total number of words + + out.clear(); + + for ( ; ; ) + { + if (p == end || isspace(*p)) + { + if (p != end) + ++p; + + if (is8bit) + { + if (prevIs8bit) + { + // No need to create a new encoded word, just append + // the current word to the previous one. + out.back().buffer() += string(start, p); + } + else + { + out.append(word(string(start, p), ch)); + prevIs8bit = true; + ++count; + } + } + else + { + if (count && !prevIs8bit) + { + out.back().buffer() += string(start, p); + } + else + { + out.append(word(string(start, p), charset(charsets::US_ASCII))); + prevIs8bit = false; + ++count; + } + } + + if (p == end) + break; + + is8bit = false; + start = p; + } + else if (!isascii(*p)) + { + is8bit = true; + ++p; + } + else + { + ++p; + } + } +} + + +// +// V-Mime Initializer +// ==================== +// +// Force instanciation of singletons. This is to prevent problems that might +// happen in multithreaded applications... +// +// WARNING: we put the initializer at the end of this compilation unit. This +// ensures this object is initialized _after_ all other global variables in +// the same compilation unit (in particular "lineLengthLimits::infinite", +// which is used by the generate() function (called from "textPartFactory" +// constructor, for example). +// + +class initializer +{ +public: + + initializer() + { + options::getInstance(); + + encoderFactory::getInstance(); + headerFieldFactory::getInstance(); + parameterFactory::getInstance(); + textPartFactory::getInstance(); + + #if VMIME_HAVE_MESSAGING_FEATURES + messaging::serviceFactory::getInstance(); + #endif + } +}; + +initializer theInitializer; + + +} // vmime diff --git a/src/base.hpp b/src/base.hpp new file mode 100644 index 00000000..a00d9c7d --- /dev/null +++ b/src/base.hpp @@ -0,0 +1,216 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_BASE_HPP_INCLUDED +#define VMIME_BASE_HPP_INCLUDED + + +#include +#include +#include +#include +#include + +#include "config.hpp" +#include "types.hpp" +#include "constants.hpp" +#include "utility/stream.hpp" + + +namespace vmime +{ + class text; + class charset; + + + // "Null" strings + extern const string NULL_STRING; +#if VMIME_WIDE_CHAR_SUPPORT + extern const wstring NULL_WSTRING; +#endif + + extern const text NULL_TEXT; + + + // + // Library name and version + // + + const string libname(); + const string libversion(); + + + // + // Helpful functions used for array -> iterator conversion + // + + template + inline T const* begin(T const (&array)[N]) + { + return (array); + } + + template + inline T const* end(T const (&array)[N]) + { + return (array + N); + } + + template + inline size_t count(T const (&array)[N]) + { + return (N); + } + + + // + // Some helpful functions + // + + bool isStringEqualNoCase(const string& s1, const char* s2, const string::size_type n); + bool isStringEqualNoCase(const string& s1, const string& s2); + bool isStringEqualNoCase(const string::const_iterator begin, const string::const_iterator end, const char* s, const string::size_type n); + + + const string toLower(const string& str); + const string trim(const string& str); + + template + const string toString(const TYPE& value) + { + std::ostringstream oss; + oss << value; + + return (oss.str()); + } + + template + const TYPE fromString(const string& value) + { + TYPE ret; + + std::istringstream iss(value); + iss >> ret; + + return (ret); + } + + + // Free the pointer elements in a STL container and empty the container + + template + void free_container(CONTAINER& c) + { + for (typename CONTAINER::iterator it = c.begin() ; it != c.end() ; ++it) + delete (*it); + + c.clear(); + } + + + // Field contents encoding (RFC-2047 and folding) + string::size_type countASCIIchars(const string::const_iterator begin, const string::const_iterator end); + + void encodeAndFoldText(utility::outputStream& os, const text& in, const string::size_type maxLineLength, const string::size_type firstLineOffset, string::size_type* lastLineLength, const int flags); + void decodeAndUnfoldText(const string& in, text& out); + void decodeAndUnfoldText(const string::const_iterator& inStart, const string::const_iterator& inEnd, text& out); + + void makeWordsFromText(const string& in, const charset& ch, text& out); + + + // + // Some constants + // + + // Flags used by "encodeAndFoldText" function + namespace encodeAndFoldFlags + { + enum + { + // If both "forceNoEncoding" and "forceEncoding" are specified, + // "forceNoEncoding" is used by default. + forceNoEncoding = (1 << 0), + forceEncoding = (1 << 1), + + noNewLineSequence = (1 << 2), + + none = 0 + }; + } + + /* + + RFC#2822 + 2.1.1. Line Length Limits + + There are two limits that this standard places on the number of + characters in a line. Each line of characters MUST be no more than + 998 characters, and SHOULD be no more than 78 characters, excluding + the CRLF. + + The 998 character limit is due to limitations in many implementations + which send, receive, or store Internet Message Format messages that + simply cannot handle more than 998 characters on a line. Receiving + implementations would do well to handle an arbitrarily large number + of characters in a line for robustness sake. However, there are so + many implementations which (in compliance with the transport + requirements of [RFC2821]) do not accept messages containing more + than 1000 character including the CR and LF per line, it is important + for implementations not to create such messages. + + The more conservative 78 character recommendation is to accommodate + the many implementations of user interfaces that display these + messages which may truncate, or disastrously wrap, the display of + more than 78 characters per line, in spite of the fact that such + implementations are non-conformant to the intent of this specification + (and that of [RFC2821] if they actually cause information to be lost). + Again, even though this limitation is put on messages, it is encumbant + upon implementations which display messages to handle an arbitrarily + large number of characters in a line (certainly at least up to the 998 + character limit) for the sake of robustness. + */ + + namespace lineLengthLimits + { + extern const string::size_type infinite; + + enum + { + max = 998, + convenient = 78 + }; + } + + + // New line sequence to be used when folding header fields. + extern const string NEW_LINE_SEQUENCE; + extern const string::size_type NEW_LINE_SEQUENCE_LENGTH; + + + // CR-LF sequence + extern const string CRLF; + + + // Mime version + extern const string MIME_VERSION; + +} // vmime + + +#endif // VMIME_BASE_HPP_INCLUDED diff --git a/src/body.cpp b/src/body.cpp new file mode 100644 index 00000000..1b5c853b --- /dev/null +++ b/src/body.cpp @@ -0,0 +1,543 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "bodyPart.hpp" +#include "body.hpp" + +#include "options.hpp" + +#include "contentTypeField.hpp" +#include "contentEncodingField.hpp" + +#include "utility/random.hpp" + +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +body::body(bodyPart& part) + : parts(*this), m_part(part), m_header(part.header()) +{ +} + + +void body::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + parts.clear(); + + // Check whether the body is a MIME-multipart + bool isMultipart = false; + string boundary; + + try + { + const contentTypeField& ctf = dynamic_cast + (m_header.fields.find(headerField::ContentType)); + + if (ctf.value().type() == mediaTypes::MULTIPART) + { + isMultipart = true; + + try + { + boundary = ctf.boundary(); + } + catch (exceptions::no_such_parameter&) + { + // No "boundary" parameter specified: we can try to + // guess it by scanning the body contents... + string::size_type pos = buffer.find("\n--", position); + + if ((pos != string::npos) && (pos < end)) + { + pos += 3; + + const string::size_type start = pos; + + char_t c = buffer[pos]; + string::size_type length = 0; + + // We have to stop after a reasonnably long boundary length (100) + // not to take the whole body contents for a boundary... + while (pos < end && length < 100 && !(c == '\r' || c == '\n')) + { + ++length; + c = buffer[pos++]; + } + + if (pos < end && length < 100) + { + // RFC #1521, Page 31: + // "...the boundary parameter, which consists of 1 to 70 + // characters from a set of characters known to be very + // robust through email gateways, and NOT ending with + // white space..." + while (pos != start && isspace(buffer[pos - 1])) + --pos; + + boundary = string(buffer.begin() + start, + buffer.begin() + pos); + } + } + } + } + } + catch (exceptions::no_such_field&) + { + // No "Content-Type" field... + } + + // This is a multi-part body + if (isMultipart && !boundary.empty()) + { + const string boundarySep("--" + boundary); + + string::size_type partStart = position; + string::size_type pos = buffer.find(boundarySep, position); + + bool lastPart = false; + + if (pos != string::npos && pos < end) + { + m_prologText = string(buffer.begin() + position, buffer.begin() + pos); + } + + for (int index = 0 ; !lastPart && (pos != string::npos) && (pos < end) ; ++index) + { + string::size_type partEnd = pos; + + // Get rid of the [CR]LF just before the boundary string + if (pos - 1 >= position && buffer[pos - 1] == '\n') --partEnd; + if (pos - 2 >= position && buffer[pos - 2] == '\r') --partEnd; + + // Check whether it is the last part (boundary terminated by "--") + pos += boundarySep.length(); + + if (pos + 1 < end && buffer[pos] == '-' && buffer[pos + 1] == '-') + { + lastPart = true; + pos += 2; + } + + // RFC #1521, Page 31: + // "...(If a boundary appears to end with white space, the + // white space must be presumed to have been added by a + // gateway, and must be deleted.)..." + while (pos < end && (buffer[pos] == ' ' || buffer[pos] == '\t')) + ++pos; + + // End of boundary line + if (pos + 1 < end && buffer[pos] == '\r' && buffer[pos + 1] =='\n') + { + pos += 2; + } + else if (pos < end && buffer[pos] == '\n') + { + ++pos; + } + + if (index > 0) + { + bodyPart* part = new bodyPart; + + try + { + part->parse(buffer, partStart, partEnd, NULL); + } + catch (std::exception&) + { + delete (part); + throw; + } + + parts.m_parts.push_back(part); + } + + partStart = pos; + pos = buffer.find(boundarySep, partStart); + } + + if (partStart < end) + m_epilogText = string(buffer.begin() + partStart, buffer.begin() + end); + } + // Treat the contents as 'simple' data + else + { + // Extract the (encoded) contents + m_contents.set(buffer, position, end, encoding()); + } + + if (newPosition) + *newPosition = end; +} + + +void body::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type /* curLinePos */, string::size_type* newLinePos) const +{ + // MIME-Multipart + if (parts.size() != 0) + { + string boundary; + + try + { + contentTypeField& ctf = dynamic_cast + (m_header.fields.find(headerField::ContentType)); + + boundary = ctf.boundary(); + } + catch (exceptions::no_such_field&) + { + // Warning: no content-type and no boundary string specified! + boundary = generateRandomBoundaryString(); + } + catch (exceptions::no_such_parameter&) + { + // Warning: no boundary string specified! + boundary = generateRandomBoundaryString(); + } + + const string& prologText = + m_prologText.empty() + ? (isRootPart() + ? options::getInstance()->multipart.prologText() + : NULL_STRING + ) + : m_prologText; + + const string& epilogText = + m_epilogText.empty() + ? (isRootPart() + ? options::getInstance()->multipart.epilogText() + : NULL_STRING + ) + : m_epilogText; + + if (!prologText.empty()) + { + encodeAndFoldText(os, text(word(prologText, charset())), maxLineLength, 0, + NULL, encodeAndFoldFlags::forceNoEncoding | encodeAndFoldFlags::noNewLineSequence); + + os << CRLF; + } + + os << "--" << boundary; + + for (std::vector ::const_iterator + p = parts.m_parts.begin() ; p != parts.m_parts.end() ; ++p) + { + os << CRLF; + + (*p)->generate(os, maxLineLength, 0); + + os << CRLF << "--" << boundary; + } + + os << "--" << CRLF; + + if (!epilogText.empty()) + { + encodeAndFoldText(os, text(word(epilogText, charset())), maxLineLength, 0, + NULL, encodeAndFoldFlags::forceNoEncoding | encodeAndFoldFlags::noNewLineSequence); + + os << CRLF; + } + + if (newLinePos) + *newLinePos = 0; + } + // Simple body + else + { + // Generate the contents + m_contents.generate(os, encoding(), maxLineLength); + } +} + + +/* + RFC #1521, Page 32: + 7.2.1. Multipart: The common syntax + + "...Encapsulation boundaries must not appear within the + encapsulations, and must be no longer than 70 characters..." + + + boundary := 0*69 bcharsnospace + + bchars := bcharsnospace / " " + + bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / "+" /"_" + / "," / "-" / "." / "/" / ":" / "=" / "?" +*/ + +const string body::generateRandomBoundaryString() +{ + // 64 characters that can be _safely_ used in a boundary string + static const char bchars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-+"; + + /* + RFC #1521, Page 19: + + Since the hyphen character ("-") is represented as itself in the + Quoted-Printable encoding, care must be taken, when encapsulating a + quoted-printable encoded body in a multipart entity, to ensure that + the encapsulation boundary does not appear anywhere in the encoded + body. (A good strategy is to choose a boundary that includes a + character sequence such as "=_" which can never appear in a quoted- + printable body. See the definition of multipart messages later in + this document.) + */ + + string::value_type boundary[2 + 48 + 1] = { 0 }; + + boundary[0] = '='; + boundary[1] = '_'; + + // Generate a string of random characters + unsigned int r = utility::random::time(); + unsigned int m = sizeof(unsigned int); + + for (size_t i = 2 ; i < (sizeof(boundary) / sizeof(boundary[0]) - 1) ; ++i) + { + boundary[i] = bchars[r & 63]; + r >>= 6; + + if (--m == 0) + { + r = utility::random::next(); + m = sizeof(unsigned int); + } + } + + return (string(boundary)); +} + + +const bool body::isValidBoundary(const string& boundary) +{ + static const string validChars("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'()+_,-./:=?"); + + const string::const_iterator end = boundary.end(); + bool valid = false; + + if (boundary.length() > 0 && boundary.length() < 70) + { + const string::value_type last = *(end - 1); + + if (!(last == ' ' || last == '\t' || last == '\n')) + { + valid = true; + + for (string::const_iterator i = boundary.begin() ; valid && i != end ; ++i) + valid = (validChars.find_first_of(*i) != string::npos); + } + } + + return (valid); +} + + +// +// Quick-access functions +// + +const mediaType body::contentType() const +{ + try + { + const contentTypeField& ctf = dynamic_cast(m_header.fields.find(headerField::ContentType)); + return (ctf.value()); + } + catch (exceptions::no_such_field&) + { + // Defaults to "text/plain" (RFC-1521) + return (mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN)); + } +} + + +const class charset body::charset() const +{ + try + { + const contentTypeField& ctf = dynamic_cast(m_header.fields.find(headerField::ContentType)); + const class charset& cs = ctf.charset(); + + return (cs); + } + catch (exceptions::no_such_parameter&) + { + // Defaults to "us-ascii" (RFC-1521) + return (vmime::charset(charsets::US_ASCII)); + } + catch (exceptions::no_such_field&) + { + // Defaults to "us-ascii" (RFC-1521) + return (vmime::charset(charsets::US_ASCII)); + } +} + + +const class encoding body::encoding() const +{ + try + { + const contentEncodingField& cef = m_header.fields.ContentTransferEncoding(); + return (cef.value()); + } + catch (exceptions::no_such_field&) + { + // Defaults to "7bit" (RFC-1521) + return (vmime::encoding(encodingTypes::SEVEN_BIT)); + } +} + + +const bool body::isRootPart() const +{ + return (m_part.parent() == NULL); +} + + +body& body::operator=(const body& b) +{ + m_prologText = b.m_prologText; + m_epilogText = b.m_epilogText; + + m_contents = b.m_contents; + + parts = b.parts; + + return (*this); +} + + +///////////////////// +// Parts container // +///////////////////// + + +body::partsContainer::partsContainer(class body& body) + : m_body(body) +{ +} + + +// Part insertion +void body::partsContainer::append(bodyPart* part) +{ + part->m_parent = &(m_body.m_part); + + m_parts.push_back(part); + + // Check whether we have a boundary string + try + { + contentTypeField& ctf = dynamic_cast + (m_body.m_header.fields.find(headerField::ContentType)); + + try + { + const string boundary = ctf.boundary(); + + if (boundary.empty() || !isValidBoundary(boundary)) + throw exceptions::no_such_parameter("boundary"); // to generate a new one + } + catch (exceptions::no_such_parameter&) + { + // No "boundary" parameter: generate a random one. + ctf.boundary() = generateRandomBoundaryString(); + } + + if (ctf.value().type() != mediaTypes::MULTIPART) + { + // Warning: multi-part body but the Content-Type is + // not specified as "multipart/..." + } + } + catch (exceptions::no_such_field&) + { + // No "Content-Type" field: create a new one and generate + // a random boundary string. + contentTypeField& ctf = dynamic_cast + (m_body.m_header.fields.get(headerField::ContentType)); + + ctf.value() = mediaType(mediaTypes::MULTIPART, mediaTypes::MULTIPART_MIXED); + ctf.boundary() = generateRandomBoundaryString(); + } +} + + +void body::partsContainer::insert(const iterator it, bodyPart* part) +{ + part->m_parent = &(m_body.m_part); + + m_parts.insert(it.m_iterator, part); +} + + +// Part removing +void body::partsContainer::remove(const iterator it) +{ + delete (*it.m_iterator); + m_parts.erase(it.m_iterator); +} + + +void body::partsContainer::clear() +{ + free_container(m_parts); +} + + +body::partsContainer::~partsContainer() +{ + clear(); +} + + +body::partsContainer& body::partsContainer::operator=(const partsContainer& c) +{ + std::vector parts; + + for (std::vector ::const_iterator it = c.m_parts.begin() ; it != c.m_parts.end() ; ++it) + { + bodyPart* p = (*it)->clone(); + p->m_parent = &(m_body.m_part); + + parts.push_back(p); + } + + for (std::vector ::iterator it = m_parts.begin() ; it != m_parts.end() ; ++it) + delete (*it); + + m_parts.resize(parts.size()); + std::copy(parts.begin(), parts.end(), m_parts.begin()); + + return (*this); +} + + +} // vmime diff --git a/src/body.hpp b/src/body.hpp new file mode 100644 index 00000000..9e43744c --- /dev/null +++ b/src/body.hpp @@ -0,0 +1,234 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_BODY_HPP_INCLUDED +#define VMIME_BODY_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "header.hpp" + +#include "mediaType.hpp" +#include "charset.hpp" +#include "encoding.hpp" + +#include "contentHandler.hpp" + + +namespace vmime +{ + + +class bodyPart; + + +/** Body section of a MIME part. + */ + +class body : public component +{ + friend class bodyPart; + +protected: + + body(bodyPart& part); + +public: + + // A sub-class for part manipulation + class partsContainer + { + friend class body; + + protected: + + partsContainer(class body& body); + ~partsContainer(); + + public: + + // Part iterator + class const_iterator; + + class iterator + { + friend class body::partsContainer::const_iterator; + friend class body::partsContainer; + + public: + + typedef std::vector ::iterator::difference_type difference_type; + + iterator(std::vector ::iterator it) : m_iterator(it) { } + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + bodyPart& operator*() const { return (**m_iterator); } + bodyPart* operator->() const { return (*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator operator++(int) { iterator i(*this); ++m_iterator; return (i); } + + iterator& operator--() { --m_iterator; return (*this); } + iterator operator--(int) { iterator i(*this); --m_iterator; return (i); } + + iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + iterator operator-(difference_type x) const { return iterator(m_iterator - x); } + + bodyPart& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector ::iterator m_iterator; + }; + + class const_iterator + { + public: + + typedef std::vector ::const_iterator::difference_type difference_type; + + const_iterator(std::vector ::const_iterator it) : m_iterator(it) { } + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const bodyPart& operator*() const { return (**m_iterator); } + const bodyPart* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator operator++(int) { const_iterator i(*this); ++m_iterator; return (i); } + + const_iterator& operator--() { --m_iterator; return (*this); } + const_iterator operator--(int) { const_iterator i(*this); --m_iterator; return (i); } + + const_iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + const_iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + const_iterator operator-(difference_type x) const { return const_iterator(m_iterator - x); } + + const bodyPart& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector ::const_iterator m_iterator; + }; + + public: + + iterator begin() { return (m_parts.begin()); } + iterator end() { return (m_parts.end()); } + + const_iterator begin() const { return (const_iterator(m_parts.begin())); } + const_iterator end() const { return (const_iterator(m_parts.end())); } + + const bodyPart& operator[](const std::vector ::size_type x) const { return (*m_parts[x]); } + bodyPart& operator[](const std::vector ::size_type x) { return (*m_parts[x]); } + + // Part insertion + void append(bodyPart* part); + void insert(const iterator it, bodyPart* part); + + // Part removing + void remove(const iterator it); + void clear(); + + // Part count + const size_t count() const { return (m_parts.size()); } + const size_t size() const { return (m_parts.size()); } + + bodyPart& front() { return (*m_parts.front()); } + const bodyPart& front() const { return (*m_parts.front()); } + bodyPart& back() { return (*m_parts.back()); } + const bodyPart& back() const { return (*m_parts.back()); } + + partsContainer& operator=(const partsContainer& c); + + protected: + + body& m_body; + + std::vector m_parts; + + } parts; + + typedef partsContainer::iterator iterator; + typedef partsContainer::const_iterator const_iterator; + + + const string& prologText() const { return (m_prologText); } + string& prologText() { return (m_prologText); } + + const string& epilogText() const { return (m_epilogText); } + string& epilogText() { return (m_epilogText); } + + const contentHandler& contents() const { return (m_contents); } + contentHandler& contents() { return (m_contents); } + + // Quick-access functions + const mediaType contentType() const; + const class charset charset() const; + const class encoding encoding() const; + + // Boundary string functions + static const string generateRandomBoundaryString(); + static const bool isValidBoundary(const string& boundary); + + body& operator=(const body& b); + +protected: + + string m_prologText; + string m_epilogText; + + contentHandler m_contents; + + bodyPart& m_part; + header& m_header; + + const bool isRootPart() const; + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_BODY_HPP_INCLUDED diff --git a/src/bodyPart.cpp b/src/bodyPart.cpp new file mode 100644 index 00000000..522a34cd --- /dev/null +++ b/src/bodyPart.cpp @@ -0,0 +1,75 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "bodyPart.hpp" + + +namespace vmime +{ + + +bodyPart::bodyPart() + : m_body(*this), m_parent(NULL) +{ +} + + +void bodyPart::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + // Parse the headers + string::size_type pos = position; + m_header.parse(buffer, pos, end, &pos); + + // Parse the body contents + m_body.parse(buffer, pos, end, NULL); + + if (newPosition) + *newPosition = end; +} + + +void bodyPart::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type /* curLinePos */, string::size_type* newLinePos) const +{ + m_header.generate(os, maxLineLength); + + os << CRLF; + + m_body.generate(os, maxLineLength); + + if (newLinePos) + *newLinePos = 0; +} + + +bodyPart* bodyPart::clone() const +{ + bodyPart* p = new bodyPart; + + p->m_parent = NULL; + p->m_header = m_header; + p->m_body = m_body; + + return (p); +} + + +} // vmime + diff --git a/src/bodyPart.hpp b/src/bodyPart.hpp new file mode 100644 index 00000000..21e59b79 --- /dev/null +++ b/src/bodyPart.hpp @@ -0,0 +1,80 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_BODYPART_HPP_INCLUDED +#define VMIME_BODYPART_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "header.hpp" +#include "body.hpp" + + +namespace vmime +{ + + +/** A MIME part. + */ + +class bodyPart : public component +{ +public: + + bodyPart(); + + const class header& header() const { return (m_header); } + class header& header() { return (m_header); } + + const class body& body() const { return (m_body); } + class body& body() { return (m_body); } + + bodyPart* parent() const { return (m_parent); } + + bodyPart* clone() const; + +protected: + + class header m_header; + class body m_body; + + bodyPart* m_parent; + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + + + // This is here because of a bug in g++ < 3.4 + friend class body; + friend class body::partsContainer; +}; + + +} // vmime + + +#endif // VMIME_BODYPART_HPP_INCLUDED diff --git a/src/charset.cpp b/src/charset.cpp new file mode 100644 index 00000000..5d5b872c --- /dev/null +++ b/src/charset.cpp @@ -0,0 +1,305 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "charset.hpp" +#include "exception.hpp" +#include "platformDependant.hpp" + + +extern "C" +{ + #include + + // HACK: prototypes may differ depending on the compiler and/or system (the + // second parameter may or may not be 'const'). This redeclaration is a hack + // to have a common prototype "iconv_cast". + typedef size_t (*iconv_const_hack)(iconv_t cd, const char* * inbuf, + size_t *inbytesleft, char* * outbuf, size_t *outbytesleft); + + #define iconv_const ((iconv_const_hack) iconv) +} + + +namespace vmime +{ + + +charset::charset() + : m_name(charsets::US_ASCII) +{ +} + + +charset::charset(const string& name) + : m_name(name) +{ +} + + +void charset::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_name = string(buffer.begin() + position, buffer.begin() + end); + + if (newPosition) + *newPosition = end; +} + + +void charset::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + os << m_name; + + if (newLinePos) + *newLinePos = curLinePos + m_name.length(); +} + + +/** Convert the contents of an input stream in a specified charset + * to another charset and write the result to an output stream. + * + * @param in input stream to read data from + * @param out output stream to write the converted data + * @param source input charset + * @param dest output charset + */ + +void charset::convert(utility::inputStream& in, utility::outputStream& out, + const charset& source, const charset& dest) +{ + // Get an iconv descriptor + const iconv_t cd = iconv_open(dest.name().c_str(), source.name().c_str()); + + if (cd != (iconv_t) -1) + { + char inBuffer[5]; + char outBuffer[32768]; + size_t inPos = 0; + + bool prevIsInvalid = false; + + while (true) + { + // Fullfill the buffer + size_t inLength = (size_t) in.read(inBuffer + inPos, sizeof(inBuffer) - inPos) + inPos; + size_t outLength = sizeof(outBuffer); + + const char* inPtr = inBuffer; + char* outPtr = outBuffer; + + // Convert input bytes + if (iconv_const(cd, &inPtr, &inLength, &outPtr, &outLength) == (size_t) -1) + { + // Illegal input sequence or input sequence has no equivalent + // sequence in the destination charset. + if (prevIsInvalid) + { + // Write successfully converted bytes + out.write(outBuffer, sizeof(outBuffer) - outLength); + + // Output a special character to indicate we don't known how to + // convert the sequence at this position + out.write("?", 1); + + // Skip a byte and leave unconverted bytes in the input buffer + std::copy((char*) inPtr + 1, inBuffer + sizeof(inBuffer), inBuffer); + inPos = inLength - 1; + } + else + { + // Write successfully converted bytes + out.write(outBuffer, sizeof(outBuffer) - outLength); + + // Leave unconverted bytes in the input buffer + std::copy((char*) inPtr, inBuffer + sizeof(inBuffer), inBuffer); + inPos = inLength; + + prevIsInvalid = true; + } + } + else + { + // Write successfully converted bytes + out.write(outBuffer, sizeof(outBuffer) - outLength); + + inPos = 0; + prevIsInvalid = false; + } + + // Check for end of data + if (in.eof() && inPos == 0) + break; + } + + // Close iconv handle + iconv_close(cd); + } + else + { + throw exceptions::charset_conv_error(); + } +} + + +/** Convert a string buffer in a specified charset to a string + * buffer in another charset. + * + * @param in input buffer + * @param out output buffer + * @param from input charset + * @param to output charset + */ + +template +void charset::iconvert(const STRINGF& in, STRINGT& out, const charset& from, const charset& to) +{ + // Get an iconv descriptor + const iconv_t cd = iconv_open(to.name().c_str(), from.name().c_str()); + + typedef typename STRINGF::value_type ivt; + typedef typename STRINGT::value_type ovt; + + if (cd != (iconv_t) -1) + { + out.clear(); + + char buffer[65536]; + + const char* inBuffer = (const char*) in.data(); + size_t inBytesLeft = in.length(); + + for ( ; inBytesLeft > 0 ; ) + { + size_t outBytesLeft = sizeof(buffer); + char* outBuffer = buffer; + + if (iconv_const(cd, &inBuffer, &inBytesLeft, + &outBuffer, &outBytesLeft) == (size_t) -1) + { + out += STRINGT((ovt*) buffer, sizeof(buffer) - outBytesLeft); + + // Ignore this "blocking" character and continue + out += '?'; + ++inBuffer; + --inBytesLeft; + } + else + { + out += STRINGT((ovt*) buffer, sizeof(buffer) - outBytesLeft); + } + } + + // Close iconv handle + iconv_close(cd); + } + else + { + throw exceptions::charset_conv_error(); + } +} + + +#if VMIME_WIDE_CHAR_SUPPORT + +/** Convert a string buffer in the specified charset to a wide-char + * string buffer. + * + * @param in input buffer + * @param out output buffer + * @param ch input charset + */ + +void charset::decode(const string& in, wstring& out, const charset& ch) +{ + iconvert(in, out, ch, charset("WCHAR_T")); +} + + +/** Convert a wide-char string buffer to a string buffer in the + * specified charset. + * + * @param in input buffer + * @param out output buffer + * @param ch output charset + */ + +void charset::encode(const wstring& in, string& out, const charset& ch) +{ + iconvert(in, out, charset("WCHAR_T"), ch); +} + +#endif + + +/** Convert a string buffer from one charset to another charset. + * + * @param in input buffer + * @param out output buffer + * @param source input charset + * @param dest output charset + */ + +void charset::convert(const string& in, string& out, const charset& source, const charset& dest) +{ + iconvert(in, out, source, dest); +} + + +/** Returns the default charset used on the system. + * + * This function simply calls platformDependantHandler::getLocaleCharset() + * and is provided for convenience. + * + * @return system default charset + */ + +const charset charset::getLocaleCharset() +{ + return (platformDependant::getHandler()->getLocaleCharset()); +} + + +charset& charset::operator=(const charset& source) +{ + m_name = source.m_name; + return (*this); +} + + +charset& charset::operator=(const string& name) +{ + parse(name); + return (*this); +} + + +const bool charset::operator==(const charset& value) const +{ + return (isStringEqualNoCase(m_name, value.m_name)); +} + + +const bool charset::operator!=(const charset& value) const +{ + return !(*this == value); +} + + +} // vmime diff --git a/src/charset.hpp b/src/charset.hpp new file mode 100644 index 00000000..4d7f8c5b --- /dev/null +++ b/src/charset.hpp @@ -0,0 +1,86 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CHARSET_HPP_INCLUDED +#define VMIME_CHARSET_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + + +namespace vmime +{ + + +/** Charset description (basic type). + */ + +class charset : public component +{ +public: + + charset(); + charset(const string& name); + +public: + + const string name() const { return (m_name); } + + charset& operator=(const charset& source); + charset& operator=(const string& name); + + const bool operator==(const charset& value) const; + const bool operator!=(const charset& value) const; + + static const charset getLocaleCharset(); + +#if VMIME_WIDE_CHAR_SUPPORT + static void decode(const string& in, wstring& out, const charset& ch); + static void encode(const wstring& in, string& out, const charset& ch); +#endif + + // In-memory conversion + static void convert(const string& in, string& out, const charset& source, const charset& dest); + + // Stream conversion + static void convert(utility::inputStream& in, utility::outputStream& out, const charset& source, const charset& dest); + +protected: + + string m_name; + + template + static void iconvert(const STRINGF& in, STRINGT& out, const charset& from, const charset& to); + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_CHARSET_HPP_INCLUDED diff --git a/src/charsetParameter.cpp b/src/charsetParameter.cpp new file mode 100644 index 00000000..ccbe7b43 --- /dev/null +++ b/src/charsetParameter.cpp @@ -0,0 +1,50 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "charsetParameter.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +void charsetParameter::parseValue(const string& buffer, const string::size_type position, + const string::size_type end) +{ + m_value.parse(buffer, position, end); +} + + +const string charsetParameter::generateValue() const +{ + return (m_value.name()); +} + + +void charsetParameter::copyFrom(const parameter& param) +{ + const charsetParameter& source = dynamic_cast(param); + m_value = source.m_value; + + defaultParameter::copyFrom(param); +} + + +} // vmime diff --git a/src/charsetParameter.hpp b/src/charsetParameter.hpp new file mode 100644 index 00000000..4e5e89d9 --- /dev/null +++ b/src/charsetParameter.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CHARSETPARAMETER_HPP_INCLUDED +#define VMIME_CHARSETPARAMETER_HPP_INCLUDED + + +#include "defaultParameter.hpp" +#include "charset.hpp" + + +namespace vmime +{ + + +class charsetParameter : public defaultParameter +{ +protected: + + charset m_value; + +public: + + void copyFrom(const parameter& param); + + const charset& value() const { return (m_value); } + charset& value() { return (m_value); } + +protected: + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_CHARSETPARAMETER_HPP_INCLUDED diff --git a/src/component.cpp b/src/component.cpp new file mode 100644 index 00000000..b8c65cd1 --- /dev/null +++ b/src/component.cpp @@ -0,0 +1,47 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "component.hpp" + +#include + + +namespace vmime +{ + + +void component::parse(const string& buffer) +{ + parse(buffer, 0, buffer.length(), NULL); +} + + +const string component::generate(const string::size_type maxLineLength, + const string::size_type curLinePos) const +{ + std::ostringstream oss; + utility::outputStreamAdapter adapter(oss); + + generate(adapter, maxLineLength, curLinePos, NULL); + + return (oss.str()); +} + + +} diff --git a/src/component.hpp b/src/component.hpp new file mode 100644 index 00000000..f7c874d7 --- /dev/null +++ b/src/component.hpp @@ -0,0 +1,84 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_COMPONENT_HPP_INCLUDED +#define VMIME_COMPONENT_HPP_INCLUDED + + +#include "base.hpp" + + +namespace vmime +{ + + +/** This abstract class is the base for all the classes in the library. + * It defines the methods for parsing and generating all the components. + */ + +class component +{ +protected: + + virtual ~component() {} + +protected: + + /** Parse RFC-822/MIME data for this component. + * + * @param buffer input buffer + */ + void parse(const string& buffer); + + /** Parse RFC-822/MIME data for this component. + * + * @param buffer input buffer + * @param position current position in the input buffer + * @param end end position in the input buffer + * @param newPosition will receive the new position in the input buffer + */ + virtual void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL) = 0; + +public: + + /** Generate RFC-2822/MIME data for this component. + * + * \deprecated Use the new generate() method, which takes an outputStream parameter. + * + * @param maxLineLength maximum line length for output + * @param curLinePos length of the current line in the output buffer + * @return generated data + */ + virtual const string generate(const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0) const; + + /** Generate RFC-2822/MIME data for this component. + * + * @param os output stream + * @param maxLineLength maximum line length for output + * @param curLinePos length of the current line in the output buffer + * @param newLinePos will receive the new line position (length of the last line written) + */ + virtual void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const = 0; +}; + + +} // vmime + + +#endif // VMIME_COMPONENT_HPP_INCLUDED diff --git a/src/constants.cpp b/src/constants.cpp new file mode 100644 index 00000000..c46c7a25 --- /dev/null +++ b/src/constants.cpp @@ -0,0 +1,151 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "constants.hpp" + + +namespace vmime +{ + + +// Media Types +namespace mediaTypes +{ + // Types + const string::value_type* const TEXT = "text"; + const string::value_type* const MULTIPART = "multipart"; + const string::value_type* const MESSAGE = "message"; + const string::value_type* const APPLICATION = "application"; + const string::value_type* const IMAGE = "image"; + const string::value_type* const AUDIO = "audio"; + const string::value_type* const VIDEO = "video"; + + // Sub-types + const string::value_type* const TEXT_PLAIN = "plain"; + const string::value_type* const TEXT_HTML = "html"; + const string::value_type* const TEXT_RICHTEXT = "richtext"; + const string::value_type* const TEXT_ENRICHED = "enriched"; + + const string::value_type* const MULTIPART_MIXED = "mixed"; + const string::value_type* const MULTIPART_RELATED = "related"; + const string::value_type* const MULTIPART_ALTERNATIVE = "alternative"; + const string::value_type* const MULTIPART_PARALLEL = "parallel"; + const string::value_type* const MULTIPART_DIGEST = "digest"; + + const string::value_type* const MESSAGE_RFC822 = "rfc822"; + const string::value_type* const MESSAGE_PARTIAL = "partial"; + const string::value_type* const MESSAGE_EXTERNAL_BODY = "external-body"; + + const string::value_type* const APPLICATION_OCTET_STREAM = "octet-stream"; + + const string::value_type* const IMAGE_JPEG = "jpeg"; + const string::value_type* const IMAGE_GIF = "gif"; + + const string::value_type* const AUDIO_BASIC = "basic"; + + const string::value_type* const VIDEO_MPEG = "mpeg"; +} + + +// Encoding types +namespace encodingTypes +{ + const string::value_type* const SEVEN_BIT = "7bit"; + const string::value_type* const EIGHT_BIT = "8bit"; + const string::value_type* const BASE64 = "base64"; + const string::value_type* const QUOTED_PRINTABLE = "quoted-printable"; + const string::value_type* const BINARY = "binary"; + const string::value_type* const UUENCODE = "uuencode"; +} + + +// Disposition types = "RFC-2183) +namespace dispositionTypes +{ + const string::value_type* const INLINE = "inline"; + const string::value_type* const ATTACHMENT = "attachment"; +} + + +// Charsets +namespace charsets +{ + const string::value_type* const ISO8859_1 = "iso-8859-1"; + const string::value_type* const ISO8859_2 = "iso-8859-2"; + const string::value_type* const ISO8859_3 = "iso-8859-3"; + const string::value_type* const ISO8859_4 = "iso-8859-4"; + const string::value_type* const ISO8859_5 = "iso-8859-5"; + const string::value_type* const ISO8859_6 = "iso-8859-6"; + const string::value_type* const ISO8859_7 = "iso-8859-7"; + const string::value_type* const ISO8859_8 = "iso-8859-8"; + const string::value_type* const ISO8859_9 = "iso-8859-9"; + const string::value_type* const ISO8859_10 = "iso-8859-10"; + const string::value_type* const ISO8859_13 = "iso-8859-13"; + const string::value_type* const ISO8859_14 = "iso-8859-14"; + const string::value_type* const ISO8859_15 = "iso-8859-15"; + const string::value_type* const ISO8859_16 = "iso-8859-16"; + + const string::value_type* const CP_437 = "cp-437"; + const string::value_type* const CP_737 = "cp-737"; + const string::value_type* const CP_775 = "cp-775"; + const string::value_type* const CP_850 = "cp-850"; + const string::value_type* const CP_852 = "cp-852"; + const string::value_type* const CP_853 = "cp-853"; + const string::value_type* const CP_855 = "cp-855"; + const string::value_type* const CP_857 = "cp-857"; + const string::value_type* const CP_858 = "cp-858"; + const string::value_type* const CP_860 = "cp-860"; + const string::value_type* const CP_861 = "cp-861"; + const string::value_type* const CP_862 = "cp-862"; + const string::value_type* const CP_863 = "cp-863"; + const string::value_type* const CP_864 = "cp-864"; + const string::value_type* const CP_865 = "cp-865"; + const string::value_type* const CP_866 = "cp-866"; + const string::value_type* const CP_869 = "cp-869"; + const string::value_type* const CP_874 = "cp-874"; + const string::value_type* const CP_1125 = "cp-1125"; + const string::value_type* const CP_1250 = "cp-1250"; + const string::value_type* const CP_1251 = "cp-1251"; + const string::value_type* const CP_1252 = "cp-1252"; + const string::value_type* const CP_1253 = "cp-1253"; + const string::value_type* const CP_1254 = "cp-1254"; + const string::value_type* const CP_1255 = "cp-1255"; + const string::value_type* const CP_1256 = "cp-1256"; + const string::value_type* const CP_1257 = "cp-1257"; + + const string::value_type* const US_ASCII = "us-ascii"; + + const string::value_type* const UTF_7 = "utf-7"; + const string::value_type* const UTF_8 = "utf-8"; + const string::value_type* const UTF_16 = "utf-16"; + const string::value_type* const UTF_32 = "utf-32"; + + const string::value_type* const WINDOWS_1250 = "windows-1250"; + const string::value_type* const WINDOWS_1251 = "windows-1251"; + const string::value_type* const WINDOWS_1252 = "windows-1252"; + const string::value_type* const WINDOWS_1253 = "windows-1253"; + const string::value_type* const WINDOWS_1254 = "windows-1254"; + const string::value_type* const WINDOWS_1255 = "windows-1255"; + const string::value_type* const WINDOWS_1256 = "windows-1256"; + const string::value_type* const WINDOWS_1257 = "windows-1257"; + const string::value_type* const WINDOWS_1258 = "windows-1258"; +} + + +} // vmime diff --git a/src/constants.hpp b/src/constants.hpp new file mode 100644 index 00000000..1970e626 --- /dev/null +++ b/src/constants.hpp @@ -0,0 +1,156 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free SOFTWARE; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software FOUNDATION; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this PROGRAM; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CONSTANTS_HPP_INCLUDED +#define VMIME_CONSTANTS_HPP_INCLUDED + + +#include + +#include "types.hpp" + + +namespace vmime +{ + // Media types (predefined types) + namespace mediaTypes + { + // Types + extern const string::value_type* const TEXT; + extern const string::value_type* const MULTIPART; + extern const string::value_type* const MESSAGE; + extern const string::value_type* const APPLICATION; + extern const string::value_type* const IMAGE; + extern const string::value_type* const AUDIO; + extern const string::value_type* const VIDEO; + + // Sub-types + extern const string::value_type* const TEXT_PLAIN; + extern const string::value_type* const TEXT_HTML; + extern const string::value_type* const TEXT_RICHTEXT; + extern const string::value_type* const TEXT_ENRICHED; + + extern const string::value_type* const MULTIPART_MIXED; + extern const string::value_type* const MULTIPART_RELATED; + extern const string::value_type* const MULTIPART_ALTERNATIVE; + extern const string::value_type* const MULTIPART_PARALLEL; + extern const string::value_type* const MULTIPART_DIGEST; + + extern const string::value_type* const MESSAGE_RFC822; + extern const string::value_type* const MESSAGE_PARTIAL; + extern const string::value_type* const MESSAGE_EXTERNAL_BODY; + + extern const string::value_type* const APPLICATION_OCTET_STREAM; + + extern const string::value_type* const IMAGE_JPEG; + extern const string::value_type* const IMAGE_GIF; + + extern const string::value_type* const AUDIO_BASIC; + + extern const string::value_type* const VIDEO_MPEG; + } + + + // Encoding types + namespace encodingTypes + { + extern const string::value_type* const SEVEN_BIT; + extern const string::value_type* const EIGHT_BIT; + extern const string::value_type* const BASE64; + extern const string::value_type* const QUOTED_PRINTABLE; + extern const string::value_type* const BINARY; + extern const string::value_type* const UUENCODE; + } + + + // Disposition types (RFC-2183) + namespace dispositionTypes + { + extern const string::value_type* const INLINE; + extern const string::value_type* const ATTACHMENT; + } + + + // Charsets + namespace charsets + { + extern const string::value_type* const ISO8859_1; + extern const string::value_type* const ISO8859_2; + extern const string::value_type* const ISO8859_3; + extern const string::value_type* const ISO8859_4; + extern const string::value_type* const ISO8859_5; + extern const string::value_type* const ISO8859_6; + extern const string::value_type* const ISO8859_7; + extern const string::value_type* const ISO8859_8; + extern const string::value_type* const ISO8859_9; + extern const string::value_type* const ISO8859_10; + extern const string::value_type* const ISO8859_13; + extern const string::value_type* const ISO8859_14; + extern const string::value_type* const ISO8859_15; + extern const string::value_type* const ISO8859_16; + + extern const string::value_type* const CP_437; + extern const string::value_type* const CP_737; + extern const string::value_type* const CP_775; + extern const string::value_type* const CP_850; + extern const string::value_type* const CP_852; + extern const string::value_type* const CP_853; + extern const string::value_type* const CP_855; + extern const string::value_type* const CP_857; + extern const string::value_type* const CP_858; + extern const string::value_type* const CP_860; + extern const string::value_type* const CP_861; + extern const string::value_type* const CP_862; + extern const string::value_type* const CP_863; + extern const string::value_type* const CP_864; + extern const string::value_type* const CP_865; + extern const string::value_type* const CP_866; + extern const string::value_type* const CP_869; + extern const string::value_type* const CP_874; + extern const string::value_type* const CP_1125; + extern const string::value_type* const CP_1250; + extern const string::value_type* const CP_1251; + extern const string::value_type* const CP_1252; + extern const string::value_type* const CP_1253; + extern const string::value_type* const CP_1254; + extern const string::value_type* const CP_1255; + extern const string::value_type* const CP_1256; + extern const string::value_type* const CP_1257; + + extern const string::value_type* const US_ASCII; + + extern const string::value_type* const UTF_7; + extern const string::value_type* const UTF_8; + extern const string::value_type* const UTF_16; + extern const string::value_type* const UTF_32; + + extern const string::value_type* const WINDOWS_1250; + extern const string::value_type* const WINDOWS_1251; + extern const string::value_type* const WINDOWS_1252; + extern const string::value_type* const WINDOWS_1253; + extern const string::value_type* const WINDOWS_1254; + extern const string::value_type* const WINDOWS_1255; + extern const string::value_type* const WINDOWS_1256; + extern const string::value_type* const WINDOWS_1257; + extern const string::value_type* const WINDOWS_1258; + } +} + + +#endif // VMIME_CONSTANTS_HPP_INCLUDED diff --git a/src/contentDispositionField.cpp b/src/contentDispositionField.cpp new file mode 100644 index 00000000..c076633f --- /dev/null +++ b/src/contentDispositionField.cpp @@ -0,0 +1,62 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "contentDispositionField.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +contentDispositionField::contentDispositionField() +{ +} + + +void contentDispositionField::parseValue(const string& buffer, const string::size_type position, + const string::size_type end) +{ + m_value.parse(buffer, position, end); +} + + +const string contentDispositionField::generateValue() const +{ + return (m_value.generate()); +} + + +contentDispositionField& contentDispositionField::operator=(const disposition& type) +{ + m_value = type; + return (*this); +} + + +void contentDispositionField::copyFrom(const headerField& field) +{ + const contentDispositionField& source = dynamic_cast(field); + m_value = source.m_value; + + parameterizedHeaderField::copyFrom(field); +} + + +} // vmime diff --git a/src/contentDispositionField.hpp b/src/contentDispositionField.hpp new file mode 100644 index 00000000..d0cf0b88 --- /dev/null +++ b/src/contentDispositionField.hpp @@ -0,0 +1,79 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CONTENTDISPOSITIONFIELD_HPP_INCLUDED +#define VMIME_CONTENTDISPOSITIONFIELD_HPP_INCLUDED + + +#include "parameterizedHeaderField.hpp" +#include "disposition.hpp" + +#include "dateParameter.hpp" +#include "textParameter.hpp" + + +namespace vmime +{ + + +class contentDispositionField : public parameterizedHeaderField +{ + friend class headerFieldFactory::registerer ; + +protected: + + contentDispositionField(); + +public: + + void copyFrom(const headerField& field); + + contentDispositionField& operator=(const disposition& type); + + const disposition& value() const { return (m_value); } + disposition& value() { return (m_value); } + + const datetime& creationDate() const { return (dynamic_cast(parameters.find("creation-date")).value()); } + datetime& creationDate() { return (dynamic_cast(parameters.get("creation-date")).value()); } + + const datetime& modificationDate() const { return (dynamic_cast(parameters.find("modification-date")).value()); } + datetime& modificationDate() { return (dynamic_cast(parameters.get("modification-date")).value()); } + + const datetime& readDate() const { return (dynamic_cast(parameters.find("read-date")).value()); } + datetime& readDate() { return (dynamic_cast(parameters.get("read-date")).value()); } + + const string& filename() const { return (dynamic_cast(parameters.find("filename")).value()); } + string& filename() { return (dynamic_cast(parameters.get("filename")).value()); } + + const string& size() const { return (dynamic_cast(parameters.find("size")).value()); } + string& size() { return (dynamic_cast(parameters.get("size")).value()); } + +protected: + + disposition m_value; + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_CONTENTDISPOSITIONFIELD_HPP_INCLUDED diff --git a/src/contentEncodingField.cpp b/src/contentEncodingField.cpp new file mode 100644 index 00000000..f16fd551 --- /dev/null +++ b/src/contentEncodingField.cpp @@ -0,0 +1,62 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "contentEncodingField.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +contentEncodingField::contentEncodingField() +{ +} + + +void contentEncodingField::parseValue(const string& buffer, const string::size_type position, + const string::size_type end) +{ + m_value.parse(buffer, position, end); +} + + +const string contentEncodingField::generateValue() const +{ + return (m_value.generate()); +} + + +contentEncodingField& contentEncodingField::operator=(const encoding& type) +{ + m_value = type; + return (*this); +} + + +void contentEncodingField::copyFrom(const headerField& field) +{ + const contentEncodingField& source = dynamic_cast(field); + m_value = source.m_value; + + parameterizedHeaderField::copyFrom(field); +} + + +} // vmime diff --git a/src/contentEncodingField.hpp b/src/contentEncodingField.hpp new file mode 100644 index 00000000..18d01ca7 --- /dev/null +++ b/src/contentEncodingField.hpp @@ -0,0 +1,61 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CONTENTENCODINGFIELD_HPP_INCLUDED +#define VMIME_CONTENTENCODINGFIELD_HPP_INCLUDED + + +#include "defaultParameterizedHeaderField.hpp" +#include "encoding.hpp" + + +namespace vmime +{ + + +class contentEncodingField : public parameterizedHeaderField +{ + friend class headerFieldFactory::registerer ; + +protected: + + contentEncodingField(); + +public: + + void copyFrom(const headerField& field); + + contentEncodingField& operator=(const encoding& type); + + const encoding& value() const { return (m_value); } + encoding& value() { return (m_value); } + +protected: + + encoding m_value; + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_CONTENTENCODINGFIELD_HPP_INCLUDED diff --git a/src/contentHandler.cpp b/src/contentHandler.cpp new file mode 100644 index 00000000..e33d0e5c --- /dev/null +++ b/src/contentHandler.cpp @@ -0,0 +1,369 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "contentHandler.hpp" + + +namespace vmime +{ + + +// No encoding = "binary" encoding +const encoding contentHandler::NO_ENCODING(encodingTypes::BINARY); + + +contentHandler::contentHandler() + : m_type(TYPE_NONE), m_encoding(NO_ENCODING), m_ownedStream(NULL), m_stream(NULL) +{ +} + + +contentHandler::contentHandler(const string& buffer, const vmime::encoding& enc) + : m_type(TYPE_STRING), m_encoding(enc), m_string(buffer), + m_ownedStream(NULL), m_stream(NULL) +{ +} + + +contentHandler::~contentHandler() +{ +} + + +contentHandler::contentHandler(const contentHandler& cts) + : m_type(cts.m_type), m_encoding(cts.m_encoding), m_string(cts.m_string), + m_ownedStream(const_cast &>(cts.m_ownedStream)), + m_stream(cts.m_stream), m_length(cts.m_length) +{ +} + + +contentHandler& contentHandler::operator=(const contentHandler& cts) +{ + m_type = cts.m_type; + m_encoding = cts.m_encoding; + + m_string = cts.m_string; + + m_ownedStream = const_cast &>(cts.m_ownedStream); + m_stream = cts.m_stream; + m_length = cts.m_length; + + return (*this); +} + + +void contentHandler::set(const utility::stringProxy& str, const vmime::encoding& enc) +{ + m_type = TYPE_STRING; + m_encoding = enc; + + m_string = str; + + m_ownedStream = NULL; + m_stream = NULL; +} + + +void contentHandler::set(const string& buffer, const vmime::encoding& enc) +{ + m_type = TYPE_STRING; + m_encoding = enc; + + m_string.set(buffer); + + m_ownedStream = NULL; + m_stream = NULL; +} + + +void contentHandler::set(const string& buffer, const string::size_type start, + const string::size_type end, const vmime::encoding& enc) +{ + m_type = TYPE_STRING; + m_encoding = enc; + + m_string.set(buffer, start, end); + + m_ownedStream = NULL; + m_stream = NULL; +} + + +void contentHandler::set(utility::inputStream* const is, const string::size_type length, + const bool own, const vmime::encoding& enc) +{ + m_type = TYPE_STREAM; + m_encoding = enc; + + m_length = length; + + if (own) + { + m_ownedStream = is; + m_stream = NULL; + } + else + { + m_ownedStream = NULL; + m_stream = is; + } + + m_string.detach(); +} + + +contentHandler& contentHandler::operator=(const string& buffer) +{ + set(buffer, NO_ENCODING); + return (*this); +} + + +void contentHandler::generate(utility::outputStream& os, const vmime::encoding& enc, + const string::size_type maxLineLength) const +{ + if (m_type == TYPE_NONE) + return; + + // Managed data is already encoded + if (isEncoded()) + { + // The data is already encoded but the encoding specified for + // the generation is different from the current one. We need + // to re-encode data: decode from input buffer to temporary + // buffer, and then re-encode to output stream... + if (m_encoding != enc) + { + utility::auto_ptr theDecoder(m_encoding.getEncoder()); + utility::auto_ptr theEncoder(enc.getEncoder()); + + theEncoder->properties()["maxlinelength"] = maxLineLength; + + switch (m_type) + { + default: + { + // No data + break; + } + case TYPE_STRING: + { + utility::inputStreamStringProxyAdapter in(m_string); + + std::ostringstream oss; + utility::outputStreamAdapter tempOut(oss); + + theDecoder->decode(in, tempOut); + + string str = oss.str(); + utility::inputStreamStringAdapter tempIn(str); + + theEncoder->encode(tempIn, os); + + break; + } + case TYPE_STREAM: + { + utility::inputStream& in = const_cast + (*(m_stream ? m_stream : m_ownedStream.ptr())); + + in.reset(); // may not work... + + std::ostringstream oss; + utility::outputStreamAdapter tempOut(oss); + + theDecoder->decode(in, tempOut); + + string str = oss.str(); + utility::inputStreamStringAdapter tempIn(str); + + theEncoder->encode(tempIn, os); + + break; + } + + } + } + // No encoding to perform + else + { + switch (m_type) + { + default: + { + // No data + break; + } + case TYPE_STRING: + { + m_string.extract(os); + break; + } + case TYPE_STREAM: + { + utility::inputStream& in = const_cast + (*(m_stream ? m_stream : m_ownedStream.ptr())); + + in.reset(); // may not work... + + utility::bufferedStreamCopy(in, os); + break; + } + + } + } + } + // Need to encode data before + else + { + utility::auto_ptr theEncoder(enc.getEncoder()); + theEncoder->properties()["maxlinelength"] = maxLineLength; + + // Encode the contents + switch (m_type) + { + default: + { + // No data + break; + } + case TYPE_STRING: + { + utility::inputStreamStringProxyAdapter in(m_string); + + theEncoder->encode(in, os); + break; + } + case TYPE_STREAM: + { + utility::inputStream& in = const_cast + (*(m_stream ? m_stream : m_ownedStream.ptr())); + + in.reset(); // may not work... + + theEncoder->encode(in, os); + break; + } + + } + } +} + + +void contentHandler::extract(utility::outputStream& os) const +{ + if (m_type == TYPE_NONE) + return; + + // No decoding to perform + if (!isEncoded()) + { + switch (m_type) + { + default: + { + // No data + break; + } + case TYPE_STRING: + { + m_string.extract(os); + break; + } + case TYPE_STREAM: + { + utility::inputStream& in = const_cast + (*(m_stream ? m_stream : m_ownedStream.ptr())); + + in.reset(); // may not work... + + utility::bufferedStreamCopy(in, os); + break; + } + + } + } + // Need to decode data + else + { + utility::auto_ptr theDecoder(m_encoding.getEncoder()); + + switch (m_type) + { + default: + { + // No data + break; + } + case TYPE_STRING: + { + utility::inputStreamStringProxyAdapter in(m_string); + + theDecoder->decode(in, os); + break; + } + case TYPE_STREAM: + { + utility::inputStream& in = const_cast + (*(m_stream ? m_stream : m_ownedStream.ptr())); + + in.reset(); // may not work... + + theDecoder->decode(in, os); + break; + } + + } + } +} + + +const string::size_type contentHandler::length() const +{ + switch (m_type) + { + case TYPE_NONE: return (0); + case TYPE_STRING: return (m_string.length()); + case TYPE_STREAM: return (m_length); + } + + return (0); +} + + +const bool contentHandler::empty() const +{ + return (m_type == TYPE_NONE); +} + + +const bool contentHandler::isEncoded() const +{ + return (m_encoding != NO_ENCODING); +} + + +const vmime::encoding& contentHandler::encoding() const +{ + return (m_encoding); +} + + +} // vmime diff --git a/src/contentHandler.hpp b/src/contentHandler.hpp new file mode 100644 index 00000000..a0ffd3d9 --- /dev/null +++ b/src/contentHandler.hpp @@ -0,0 +1,125 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CONTENTHANDLER_HPP_INCLUDED +#define VMIME_CONTENTHANDLER_HPP_INCLUDED + + +#include + +#include "base.hpp" +#include "utility/stringProxy.hpp" +#include "utility/smartPtr.hpp" +#include "encoding.hpp" + + +namespace vmime +{ + + +class contentHandler +{ +private: + + static const vmime::encoding NO_ENCODING; + +public: + + contentHandler(); + contentHandler(const string& buffer, const vmime::encoding& enc = NO_ENCODING); // for compatibility + ~contentHandler(); + + // Copy + contentHandler(const contentHandler& cts); + contentHandler& operator=(const contentHandler& cts); + + // Set the data contained in the body. + // + // The two first functions take advantage of the COW (copy-on-write) system that + // might be implemented into std::string. This is done using "stringProxy" object. + // + // Set "enc" parameter to anything other than NO_ENCODING if the data managed by + // this content handler is already encoded with the specified encoding (so, no + // encoding/decoding will be performed on generate()/extract()). Note that the + // data may be re-encoded (that is, decoded and encoded) if the encoding passed + // to generate() is different from this one... + // + // The 'length' parameter is optional (user-defined). You can pass 0 if you want, + // VMime does not make use of it. + void set(const utility::stringProxy& str, const vmime::encoding& enc = NO_ENCODING); + void set(const string& buffer, const vmime::encoding& enc = NO_ENCODING); + void set(const string& buffer, const string::size_type start, const string::size_type end, const vmime::encoding& enc = NO_ENCODING); + void set(utility::inputStream* const is, const utility::stream::size_type length, const bool own, const vmime::encoding& enc = NO_ENCODING); + + // For compatibility + contentHandler& operator=(const string& buffer); + + // WRITE: Output the contents into the specified stream. Data will be + // encoded before being written into the stream. This is used internally + // by the body object to generate the message, you may not need to use + // this (see function extract() if you want to get the contents). + void generate(utility::outputStream& os, const vmime::encoding& enc, const string::size_type maxLineLength = lineLengthLimits::infinite) const; + + // READ: Extract the contents into the specified stream. If needed, data + // will be decoded before being written into the stream. + void extract(utility::outputStream& os) const; + + // Returns the actual length of the data. WARNING: this can return 0 if no + // length was specified when setting data of this object. + const string::size_type length() const; + + // Returns 'true' if the data managed by this object is encoded. + const bool isEncoded() const; + + // Returns the encoding used for the data (or "binary" if not encoded). + const vmime::encoding& encoding() const; + + // Returns 'true' if there is no data set. + const bool empty() const; + +private: + + // Source of data managed by this content handler + enum Types + { + TYPE_NONE, + TYPE_STRING, + TYPE_STREAM + }; + + Types m_type; + + // Equals to NO_ENCODING if data is not encoded, otherwise this + // specifies the encoding that have been used to encode the data. + vmime::encoding m_encoding; + + // Used if m_type == TYPE_STRING + utility::stringProxy m_string; + + // Used if m_type == TYPE_STREAM + utility::smart_ptr m_ownedStream; // 'contentHandler' objects are copiable... + utility::inputStream* m_stream; + string::size_type m_length; +}; + + +} // vmime + + +#endif // VMIME_CONTENTHANDLER_HPP_INCLUDED diff --git a/src/contentTypeField.cpp b/src/contentTypeField.cpp new file mode 100644 index 00000000..3149606e --- /dev/null +++ b/src/contentTypeField.cpp @@ -0,0 +1,62 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "contentTypeField.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +contentTypeField::contentTypeField() +{ +} + + +void contentTypeField::parseValue(const string& buffer, + const string::size_type position, const string::size_type end) +{ + m_value.parse(buffer, position, end); +} + + +const string contentTypeField::generateValue() const +{ + return (m_value.generate()); +} + + +contentTypeField& contentTypeField::operator=(const mediaType& type) +{ + m_value = type; + return (*this); +} + + +void contentTypeField::copyFrom(const headerField& field) +{ + const contentTypeField& source = dynamic_cast(field); + m_value = source.m_value; + + parameterizedHeaderField::copyFrom(field); +} + + +} // vmime diff --git a/src/contentTypeField.hpp b/src/contentTypeField.hpp new file mode 100644 index 00000000..5244e454 --- /dev/null +++ b/src/contentTypeField.hpp @@ -0,0 +1,72 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_CONTENTTYPEFIELD_HPP_INCLUDED +#define VMIME_CONTENTTYPEFIELD_HPP_INCLUDED + + +#include "parameterizedHeaderField.hpp" + +#include "mediaType.hpp" +#include "charset.hpp" + +#include "textParameter.hpp" +#include "charsetParameter.hpp" + + +namespace vmime +{ + + +class contentTypeField : public parameterizedHeaderField +{ + friend class headerFieldFactory::registerer ; + +protected: + + contentTypeField(); + +public: + + void copyFrom(const headerField& field); + + contentTypeField& operator=(const mediaType& type); + + const mediaType& value() const { return (m_value); } + mediaType& value() { return (m_value); } + + const string& boundary() const { return (dynamic_cast(parameters.find("boundary")).value()); } + string& boundary() { return (dynamic_cast(parameters.get("boundary")).value()); } + + const class charset& charset() const { return (dynamic_cast(parameters.find("charset")).value()); } + class charset& charset() { return (dynamic_cast(parameters.get("charset")).value()); } + +protected: + + mediaType m_value; + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_CONTENTTYPEFIELD_HPP_INCLUDED diff --git a/src/dateField.cpp b/src/dateField.cpp new file mode 100644 index 00000000..ed673505 --- /dev/null +++ b/src/dateField.cpp @@ -0,0 +1,66 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "dateField.hpp" + + +namespace vmime +{ + + +dateField::dateField() +{ +} + + +void dateField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_datetime.parse(buffer, position, end, newPosition); +} + + +void dateField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + m_datetime.generate(os, maxLineLength, pos, newLinePos); +} + + +dateField& dateField::operator=(const class datetime& datetime) +{ + m_datetime = datetime; + return (*this); +} + + +void dateField::copyFrom(const headerField& field) +{ + const dateField& source = dynamic_cast(field); + m_datetime = source.m_datetime; + + headerField::copyFrom(field); +} + + +} // vmime diff --git a/src/dateField.hpp b/src/dateField.hpp new file mode 100644 index 00000000..76f47128 --- /dev/null +++ b/src/dateField.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DATEFIELD_HPP_INCLUDED +#define VMIME_DATEFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "dateTime.hpp" + + +namespace vmime +{ + + +class dateField : public headerField +{ + friend class headerFieldFactory::registerer ; + +protected: + + dateField(); + +public: + + void copyFrom(const headerField& field); + + dateField& operator=(const class datetime& datetime); + + const datetime& value() const { return (m_datetime); } + datetime& value() { return (m_datetime); } + +protected: + + datetime m_datetime; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_DATEFIELD_HPP_INCLUDED diff --git a/src/dateParameter.cpp b/src/dateParameter.cpp new file mode 100644 index 00000000..f0f4d8cd --- /dev/null +++ b/src/dateParameter.cpp @@ -0,0 +1,50 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "dateParameter.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +void dateParameter::parseValue(const string& buffer, const string::size_type position, + const string::size_type end) +{ + m_value.parse(buffer, position, end); +} + + +const string dateParameter::generateValue() const +{ + return (m_value.generate()); +} + + +void dateParameter::copyFrom(const parameter& param) +{ + const dateParameter& source = dynamic_cast(param); + m_value = source.m_value; + + defaultParameter::copyFrom(param); +} + + +} // vmime diff --git a/src/dateParameter.hpp b/src/dateParameter.hpp new file mode 100644 index 00000000..aa4ffd63 --- /dev/null +++ b/src/dateParameter.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DATEPARAMETER_HPP_INCLUDED +#define VMIME_DATEPARAMETER_HPP_INCLUDED + + +#include "defaultParameter.hpp" +#include "dateTime.hpp" + + +namespace vmime +{ + + +class dateParameter : public defaultParameter +{ +protected: + + datetime m_value; + +public: + + void copyFrom(const parameter& param); + + const datetime& value() const { return (m_value); } + datetime& value() { return (m_value); } + +protected: + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_DATEPARAMETER_HPP_INCLUDED diff --git a/src/dateTime.cpp b/src/dateTime.cpp new file mode 100644 index 00000000..ceaec6f1 --- /dev/null +++ b/src/dateTime.cpp @@ -0,0 +1,702 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include + +#include "dateTime.hpp" +#include "platformDependant.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + +/* + + RFC #822: + 5. DATE AND TIME SPECIFICATION + +date-time = [ day "," ] date time ; dd mm yy + ; hh:mm:ss zzz +day = "Mon" / "Tue" / "Wed" / "Thu" / + "Fri" / "Sat" / "Sun" + +date = 1*2DIGIT month 2DIGIT ; day month year + ; e.g. 20 Jun 82 +month = "Jan" / "Feb" / "Mar" / "Apr" / + "May" / "Jun" / "Jul" / "Aug" / + "Sep" / "Oct" / "Nov" / "Dec" + +time = hour zone ; ANSI and Military + +hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT] ; 00:00:00 - 23:59:59 + +zone = "UT" / "GMT" ; Universal Time + ; North American : UT + / "EST" / "EDT" ; Eastern: - 5/ - 4 + / "CST" / "CDT" ; Central: - 6/ - 5 + / "MST" / "MDT" ; Mountain: - 7/ - 6 + / "PST" / "PDT" ; Pacific: - 8/ - 7 + / 1ALPHA ; Military: Z = UT; + ; A:-1; (J not used) + ; M:-12; N:+1; Y:+12 + / ( ("+" / "-") 4DIGIT ) ; Local differential + ; hours+min. (HHMM) +*/ + + +void datetime::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* p = buffer.data() + position; + + // Parse the date and time value + while (p < pend && isspace(*p)) ++p; + + if (p < pend) + { + if (isalpha(*p)) + { + // Ignore week day + while (p < pend && isalpha(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + if (p < pend && *p == ',') ++p; + while (p < pend && isspace(*p)) ++p; + } + + while (p < pend && !isdigit(*p)) ++p; + + if (p < pend && isdigit(*p)) + { + // Month day + comp_t day = 0; + + do + { + day = day * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + m_day = (day >= 1 && day <= 31) ? day : 1; + + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + else + { + m_day = 1; + + // Skip everything to the next field + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + + if (p < pend && isalpha(*p)) + { + // Month + char_t month[4] = { 0 }; + int monthLength = 0; + + do + { + month[monthLength++] = *p; + ++p; + } + while (monthLength < 3 && p < pend && isalpha(*p)); + + while (p < pend && isalpha(*p)) ++p; + + switch (month[0]) + { + case 'a': + case 'A': + { + if (month[1] == 'u' || month[1] == 'U') + m_month = AUGUST; + else + m_month = APRIL; // by default + + break; + } + case 'd': + case 'D': + { + m_month = DECEMBER; + break; + } + case 'f': + case 'F': + { + m_month = FEBRUARY; + break; + } + case 'j': + case 'J': + { + if (month[1] == 'u' || month[1] == 'U') + { + if (month[2] == 'l' || month[2] == 'L') + m_month = JULY; + else // if (month[2] == 'n' || month[2] == 'N') + m_month = JUNE; + } + else + { + m_month = JANUARY; // by default + } + + break; + } + case 'm': + case 'M': + { + if ((month[1] == 'a' || month[1] == 'A') && + (month[2] == 'y' || month[2] == 'Y')) + { + m_month = MAY; + } + else + { + m_month = MARCH; // by default + } + + break; + } + case 'n': + case 'N': + { + m_month = NOVEMBER; + break; + } + case 'o': + case 'O': + { + m_month = OCTOBER; + break; + } + case 's': + case 'S': + { + m_month = SEPTEMBER; + break; + } + default: + { + m_month = JANUARY; // by default + break; + } + + } + + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + else + { + m_month = JANUARY; + + // Skip everything to the next field + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + + if (p < pend && isdigit(*p)) + { + // Year + comp_t year = 0; + + do + { + year = year * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + if (year < 70) m_year = year + 2000; + else if (year < 1000) m_year = year + 1900; + else m_year = year; + + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + else + { + m_year = 1970; + + // Skip everything to the next field + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + + if (p < pend && isdigit(*p)) + { + // Hour + comp_t hour = 0; + + do + { + hour = hour * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + m_hour = (hour >= 0 && hour <= 23) ? hour : 0; + + while (p < pend && isspace(*p)) ++p; + + if (p < pend && *p == ':') + { + ++p; + + while (p < pend && isspace(*p)) ++p; + + if (p < pend && isdigit(*p)) + { + // Minute + comp_t minute = 0; + + do + { + minute = minute * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + m_minute = (minute >= 0 && minute <= 59) ? minute : 0; + + while (p < pend && isspace(*p)) ++p; + + if (p < pend && *p == ':') + { + ++p; + + while (p < pend && isspace(*p)) ++p; + + if (p < pend && isdigit(*p)) + { + // Second + comp_t second = 0; + + do + { + second = second * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + m_second = (second >= 0 && second <= 59) ? second : 0; + + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + else + { + m_second = 0; + } + } + else + { + m_second = 0; + } + } + else + { + m_minute = 0; + } + } + else + { + m_minute = 0; + } + } + else + { + m_hour = 0; + + // Skip everything to the next field + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + + if (p + 1 < pend && (*p == '+' || *p == '-') && isdigit(*(p + 1))) + { + const char_t sign = *p; + ++p; + + // Zone offset (in hour/minutes) + comp_t offset = 0; + + do + { + offset = offset * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + const comp_t hourOff = offset / 100; + const comp_t minOff = offset % 100; + + if (sign == '+') + m_zone = hourOff * 60 + minOff; + else + m_zone = -(hourOff * 60 + minOff); + } + else if (p < pend && isalpha(*p)) + { + bool done = false; + + // Zone offset (Time zone name) + char_t zone[4] = { 0 }; + int zoneLength = 0; + + do + { + zone[zoneLength++] = *p; + ++p; + } + while (zoneLength < 3 && p < pend && isdigit(*p)); + + switch (zone[0]) + { + case 'c': + case 'C': + { + if (zoneLength >= 2) + { + if (zone[1] == 's' || zone[1] == 'S') + m_zone = CST; + else + m_zone = CDT; + + done = true; + } + + break; + } + case 'e': + case 'E': + { + if (zoneLength >= 2) + { + if (zone[1] == 's' || zone[1] == 'S') + m_zone = EST; + else + m_zone = EDT; + + done = true; + } + + break; + } + case 'm': + case 'M': + { + if (zoneLength >= 2) + { + if (zone[1] == 's' || zone[1] == 'S') + m_zone = MST; + else + m_zone = MDT; + + done = true; + } + + break; + } + case 'p': + case 'P': + { + if (zoneLength >= 2) + { + if (zone[1] == 's' || zone[1] == 'S') + m_zone = PST; + else + m_zone = PDT; + + done = true; + } + + break; + } + case 'u': + case 'U': + { + if (zoneLength >= 2) + { + m_zone = GMT; // = UTC + done = true; + } + + break; + } + + } + + if (!done) + { + const char_t z = zone[0]; + + // Military time zone + if (z != 'j' && z != 'J') + { + typedef std::map Map; + static const Map::value_type offsetMapInit[] = + { + Map::value_type('a', -60), + Map::value_type('b', -120), + Map::value_type('c', -180), + Map::value_type('d', -240), + Map::value_type('e', -300), + Map::value_type('f', -360), + Map::value_type('g', -420), + Map::value_type('h', -480), + Map::value_type('i', -540), + Map::value_type('k', -600), + Map::value_type('l', -660), + Map::value_type('m', -720), + + Map::value_type('n', 60), + Map::value_type('o', 120), + Map::value_type('p', 180), + Map::value_type('q', 240), + Map::value_type('r', 300), + Map::value_type('s', 360), + Map::value_type('t', 420), + Map::value_type('u', 480), + Map::value_type('v', 540), + Map::value_type('w', 600), + Map::value_type('x', 660), + Map::value_type('y', 720), + + Map::value_type('z', 0), + }; + static const Map offsetMap + (::vmime::begin(offsetMapInit), + ::vmime::end(offsetMapInit)); + + Map::const_iterator pos = + offsetMap.find(tolower(z)); + + if (pos != offsetMap.end()) + m_zone = (*pos).second; + else + m_zone = GMT; + } + else + { + m_zone = GMT; + } + } + } + else + { + m_zone = 0; + } + } + else + { + m_year = 1970; + m_month = JANUARY; + m_day = 1; + + m_hour = 0; + m_minute = 0; + m_second = 0; + + m_zone = 0; + } + + if (newPosition) + *newPosition = end; +} + + +void datetime::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + static const string::value_type* dayNames[] = + { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + static const string::value_type* monthNames[] = + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + const comp_t z = ((m_zone < 0) ? -m_zone : m_zone); + const comp_t zh = z / 60; + const comp_t zm = z % 60; + + std::ostringstream oss; + oss << dayNames[dayOfWeek(m_year, m_month, m_day)] << ", " + << m_day << " " << monthNames[month() - 1] << " " << year() + << " " << std::setfill('0') << std::setw(2) << m_hour << ":" + << std::setfill('0') << std::setw(2) << m_minute << ":" + << std::setfill('0') << std::setw(2) << m_second + << " " << ((m_zone < 0) ? '-' : '+') << std::setfill('0') << std::setw(2) << zh + << std::setfill('0') << std::setw(2) << zm; + + const string& str = oss.str(); + os << str; + + if (newLinePos) + *newLinePos = curLinePos + str.length(); +} + + +datetime::datetime() + : m_year(1970), m_month(1), m_day(1), + m_hour(0), m_minute(0), m_second(0), m_zone(0) +{ +} + + +datetime::datetime(const comp_t year, const comp_t month, const comp_t day) + : m_year(year), m_month(month), m_day(day), + m_hour(0), m_minute(0), m_second(0), m_zone(0) +{ +} + + +datetime::datetime(const comp_t year, const comp_t month, const comp_t day, + const comp_t hour, const comp_t minute, const comp_t second, + const comp_t zone) + : m_year(year), m_month(month), m_day(day), + m_hour(hour), m_minute(minute), m_second(second), m_zone(zone) +{ +} + + +datetime::datetime(const datetime& d) + : component(), m_year(d.m_year), m_month(d.m_month), m_day(d.m_day), + m_hour(d.m_hour), m_minute(d.m_minute), m_second(d.m_second), m_zone(d.m_zone) +{ +} + + +datetime::datetime(const string& date) +{ + parse(date); +} + + +datetime::~datetime() +{ +} + + +void datetime::copyFrom(const datetime& d) +{ + m_year = d.m_year; + m_month = d.m_month; + m_day = d.m_day; + m_hour = d.m_hour; + m_minute = d.m_minute; + m_second = d.m_second; + m_zone = d.m_zone; +} + + +datetime& datetime::operator=(const datetime& d) +{ + copyFrom(d); + return (*this); +} + + +datetime& datetime::operator=(const string& s) +{ + parse(s); + return (*this); +} + + +void datetime::getTime(comp_t& hour, comp_t& minute, comp_t& second, comp_t& zone) const +{ + hour = m_hour; + minute = m_minute; + second = m_second; + zone = m_zone; +} + + +void datetime::getTime(comp_t& hour, comp_t& minute, comp_t& second) const +{ + hour = m_hour; + minute = m_minute; + second = m_second; +} + + +void datetime::getDate(comp_t& year, comp_t& month, comp_t& day) const +{ + year = m_year; + month = m_month; + day = m_day; +} + + +void datetime::setTime(const comp_t hour, const comp_t minute, + const comp_t second, const comp_t zone) +{ + m_hour = hour; + m_minute = minute; + m_second = second; + m_zone = zone; +} + + +void datetime::setDate(const comp_t year, const comp_t month, const comp_t day) +{ + m_year = year; + m_month = month; + m_day = day; +} + + +const datetime::comp_t datetime::dayOfWeek(const comp_t year, const comp_t month, const comp_t day) +{ + comp_t y = year; + comp_t m = month; + + // From RFC-3339 - Appendix B. Day of the Week + + // Adjust months so February is the last one + m -= 2; + + if (m < 1) + { + m += 12; + --y; + } + + // Split by century + const comp_t cent = y / 100; + y %= 100; + + return (((26 * m - 2) / 10 + day + y + (y >> 2) + (cent >> 2) + 5 * cent) % 7); +} + + +const datetime datetime::now() +{ + return (platformDependant::getHandler()->getCurrentLocalTime()); +} + + +} // vmime diff --git a/src/dateTime.hpp b/src/dateTime.hpp new file mode 100644 index 00000000..b50e6483 --- /dev/null +++ b/src/dateTime.hpp @@ -0,0 +1,242 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DATETIME_HPP_INCLUDED +#define VMIME_DATETIME_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + + +namespace vmime +{ + + +/** Date and time (basic type). + */ + +class datetime : public component +{ +public: + + // Data type for a date/time component + typedef int comp_t; + + // Constructors + datetime(); + datetime(const comp_t year, const comp_t month, const comp_t day); + datetime(const comp_t year, const comp_t month, const comp_t day, const comp_t hour, const comp_t minute, const comp_t second, const comp_t zone = GMT); + datetime(const datetime& d); + datetime(const string& date); + + // Destructor + ~datetime(); + + // Some time zones (in minutes) + enum TimeZones + { + GMT_12 = -720, // GMT-12h + GMT_11 = -660, // GMT-11h + GMT_10 = -600, // GMT-10h + GMT_9 = -540, // GMT-9h + GMT_8 = -480, // GMT-8h + GMT_7 = -420, // GMT-7h + GMT_6 = -360, // GMT-6h + GMT_5 = -300, // GMT-5h + GMT_4 = -240, // GMT-4h + GMT_3 = -180, // GMT-3h + GMT_2 = -120, // GMT-2h + GMT_1 = -60, // GMT-1h + GMT = 0, // GMT + GMT1 = 60, // GMT+1h + GMT2 = 120, // GMT+2h + GMT3 = 180, // GMT+3h + GMT4 = 240, // GMT+4h + GMT5 = 300, // GMT+5h + GMT6 = 360, // GMT+6h + GMT7 = 420, // GMT+7h + GMT8 = 480, // GMT+8h + GMT9 = 540, // GMT+9h + GMT10 = 600, // GMT+10h + GMT11 = 660, // GMT+11h + GMT12 = 720, // GMT+12h + + UT = GMT, // Universal Time + + EST = GMT_5, // Eastern + EDT = GMT_4, + CST = GMT_6, // Central + CDT = GMT_5, + MST = GMT_7, // Mountain + MDT = GMT_6, + PST = GMT_8, // Pacific + PDT = GMT_7, + + // Military time zones + A = GMT_1, + B = GMT_2, + C = GMT_3, + D = GMT_4, + E = GMT_5, + F = GMT_6, + G = GMT_7, + H = GMT_8, + I = GMT_9, // J not used + K = GMT_10, + L = GMT_11, + M = GMT_12, + + N = GMT1, + O = GMT2, + P = GMT3, + Q = GMT4, + R = GMT5, + S = GMT6, + T = GMT7, + U = GMT8, + V = GMT9, + W = GMT10, + X = GMT11, + Y = GMT12, + + Z = GMT + }; + + // Months list + enum Months + { + // Long + JANUARY = 1, + FEBRUARY = 2, + MARCH = 3, + APRIL = 4, + MAY = 5, + JUNE = 6, + JULY = 7, + AUGUST = 8, + SEPTEMBER = 9, + OCTOBER = 10, + NOVEMBER = 11, + DECEMBER = 12, + + // Short + JAN = 1, + FEB = 2, + MAR = 3, + APR = 4, + JUN = 6, + JUL = 7, + AUG = 8, + SEP = 9, + OCT = 10, + NOV = 11, + DEC = 12 + }; + + // Days of week list + enum DaysOfWeek + { + // Long + SUNDAY = 0, + MONDAY = 1, + TUESDAY = 2, + WEDNESDAY = 3, + THURSDAY = 4, + FRIDAY = 5, + SATURDAY = 6, + + // Short + SUN = 0, + MON = 1, + TUE = 2, + WED = 3, + THU = 4, + FRI = 5, + SAT = 6 + }; + +protected: + + // Date components + comp_t m_year; + comp_t m_month; + comp_t m_day; + + // Time components + comp_t m_hour; + comp_t m_minute; + comp_t m_second; + comp_t m_zone; + +public: + + // Get + const comp_t year() const { return (m_year); } + const comp_t month() const { return (m_month); } + const comp_t day() const { return (m_day); } + const comp_t hour() const { return (m_hour); } + const comp_t minute() const { return (m_minute); } + const comp_t second() const { return (m_second); } + const comp_t zone() const { return (m_zone); } + + void getTime(comp_t& hour, comp_t& minute, comp_t& second, comp_t& zone) const; + void getTime(comp_t& hour, comp_t& minute, comp_t& second) const; + void getDate(comp_t& year, comp_t& month, comp_t& day) const; + + // Set + comp_t& year() { return (m_year); } + comp_t& month() { return (m_month); } + comp_t& day() { return (m_day); } + comp_t& hour() { return (m_hour); } + comp_t& minute() { return (m_minute); } + comp_t& second() { return (m_second); } + comp_t& zone() { return (m_zone); } + + void setTime(const comp_t hour = 0, const comp_t minute = 0, const comp_t second = 0, const comp_t zone = GMT); + void setDate(const comp_t year, const comp_t month, const comp_t day); + + // Assignment + datetime& operator=(const datetime& d); + datetime& operator=(const string& s); + + void copyFrom(const datetime& d); + + // Current date and time + static const datetime now(); + +protected: + + static const comp_t dayOfWeek(const comp_t year, const comp_t month, const comp_t day); + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_DATETIME_HPP_INCLUDED diff --git a/src/defaultAttachment.cpp b/src/defaultAttachment.cpp new file mode 100644 index 00000000..20f649cb --- /dev/null +++ b/src/defaultAttachment.cpp @@ -0,0 +1,91 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "defaultAttachment.hpp" +#include "encoding.hpp" + + +namespace vmime +{ + + +defaultAttachment::defaultAttachment() +{ +} + + +defaultAttachment::defaultAttachment(const contentHandler& data, + const class encoding& enc, const mediaType& type, const text& desc) + : m_type(type), m_desc(desc), m_data(data), m_encoding(enc) +{ +} + + +defaultAttachment::defaultAttachment(const contentHandler& data, + const mediaType& type, const text& desc) + : m_type(type), m_desc(desc), m_data(data), m_encoding(encoding::decide(data)) +{ +} + + +defaultAttachment::defaultAttachment(const defaultAttachment& attach) + : attachment(), m_type(attach.m_type), m_desc(attach.m_desc), + m_data(attach.m_data), m_encoding(attach.m_encoding) +{ +} + + +attachment& defaultAttachment::operator=(const attachment& attach) +{ + const defaultAttachment& att = + dynamic_cast (attach); + + m_type = att.m_type; + m_desc = att.m_desc; + m_data = att.m_data; + m_encoding = att.m_encoding; + + return (*this); +} + + +void defaultAttachment::generateIn(bodyPart& parent) const +{ + // Create and append a new part for this attachment + bodyPart* part = new bodyPart; + parent.body().parts.append(part); + + generatePart(*part); +} + + +void defaultAttachment::generatePart(bodyPart& part) const +{ + // Set header fields + part.header().fields.ContentType() = m_type; + if (!m_desc.empty()) part.header().fields.ContentDescription() = m_desc; + part.header().fields.ContentTransferEncoding() = m_encoding; + part.header().fields.ContentDisposition() = disposition(dispositionTypes::ATTACHMENT); + + // Set contents + part.body().contents() = m_data; +} + + +} // vmime diff --git a/src/defaultAttachment.hpp b/src/defaultAttachment.hpp new file mode 100644 index 00000000..2cf11638 --- /dev/null +++ b/src/defaultAttachment.hpp @@ -0,0 +1,69 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DEFAULTATTACHMENT_HPP_INCLUDED +#define VMIME_DEFAULTATTACHMENT_HPP_INCLUDED + + +#include "attachment.hpp" +#include "encoding.hpp" + + +namespace vmime +{ + + +class defaultAttachment : public attachment +{ +protected: + + // For use in derived classes. + defaultAttachment(); + +public: + + defaultAttachment(const contentHandler& data, const class encoding& enc, const mediaType& type, const text& desc = NULL_TEXT); + defaultAttachment(const contentHandler& data, const mediaType& type, const text& desc = NULL_TEXT); + defaultAttachment(const defaultAttachment& attach); + + attachment& operator=(const attachment& attach); + + const mediaType& type() const { return (m_type); } + const text& description() const { return (m_desc); } + const contentHandler& data() const { return (m_data); } + const class encoding& encoding() const { return (m_encoding); } + +protected: + + mediaType m_type; // Media type (eg. "application/octet-stream") + text m_desc; // Description (eg. "The image you requested") + contentHandler m_data; // Attachment data (eg. the file contents) + class encoding m_encoding; // Encoding + + // No need to override "generateIn", use "generatePart" instead (see below). + void generateIn(bodyPart& parent) const; + + virtual void generatePart(bodyPart& part) const; +}; + + +} // vmime + + +#endif // VMIME_DEFAULTATTACHMENT_HPP_INCLUDED diff --git a/src/defaultField.cpp b/src/defaultField.cpp new file mode 100644 index 00000000..77392725 --- /dev/null +++ b/src/defaultField.cpp @@ -0,0 +1,71 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "defaultField.hpp" +#include "text.hpp" + + +namespace vmime +{ + + +defaultField::defaultField() +{ +} + + +void defaultField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_text = string(buffer.begin() + position, buffer.begin() + end); + + if (newPosition) + *newPosition = end; +} + + +void defaultField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + encodeAndFoldText(os, vmime::text(word(m_text, charset())), maxLineLength, + pos, newLinePos, encodeAndFoldFlags::forceNoEncoding); +} + + +defaultField& defaultField::operator=(const string& text) +{ + m_text = text; + return (*this); +} + + +void defaultField::copyFrom(const headerField& field) +{ + const defaultField& source = dynamic_cast(field); + m_text = source.m_text; + + headerField::copyFrom(field); +} + + +} // vmime diff --git a/src/defaultField.hpp b/src/defaultField.hpp new file mode 100644 index 00000000..c855ca8c --- /dev/null +++ b/src/defaultField.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DEFAULTFIELD_HPP_INCLUDED +#define VMIME_DEFAULTFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" + + +namespace vmime +{ + + +class defaultField : public headerField +{ + friend class headerFieldFactory; + friend class headerFieldFactory::registerer ; + +protected: + + defaultField(); + +public: + + void copyFrom(const headerField& field); + + defaultField& operator=(const string& text); + + const string& value() const { return (m_text); } + string& value() { return (m_text); } + +protected: + + string m_text; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_DEFAULTFIELD_HPP_INCLUDED diff --git a/src/defaultParameter.cpp b/src/defaultParameter.cpp new file mode 100644 index 00000000..2d9e72db --- /dev/null +++ b/src/defaultParameter.cpp @@ -0,0 +1,117 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "defaultParameter.hpp" + + +namespace vmime +{ + + +void defaultParameter::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + parseValue(buffer, position, end); + + if (newPosition) + *newPosition = end; +} + + +void defaultParameter::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + const string value = quotedValue(); + + pos += m_name.length() + value.length() + 2; + + if (pos > maxLineLength) + { + os << NEW_LINE_SEQUENCE; + pos = NEW_LINE_SEQUENCE_LENGTH; + } + + os << m_name << "=" << value; + + if (newLinePos) + *newLinePos = pos; +} + + +const string defaultParameter::quotedValue() const +{ + const string value(generateValue()); + + std::ostringstream ss; + string::const_iterator start = value.begin(); + bool quoted = false; + + for (string::const_iterator i = value.begin() ; i != value.end() ; ++i) + { + switch (*i) + { + // Characters that need to be quoted _and_ escaped + case '"': + case '\\': + + ss << string(start, i) << "\\" << *i; + + start = i + 1; + quoted = true; + + break; + + // Other characters that need quoting + case ' ': + case '\t': + case '(': + case ')': + case '<': + case '>': + case '@': + case ',': + case ';': + case ':': + case '/': + case '[': + case ']': + case '?': + case '=': + + quoted = true; + break; + } + } + + if (start != value.end()) + ss << string(start, value.end()); + + return (quoted ? ("\"" + ss.str() + "\"") : (ss.str())); +} + + +void defaultParameter::copyFrom(const parameter& param) +{ + parameter::copyFrom(param); +} + + +} // vmime diff --git a/src/defaultParameter.hpp b/src/defaultParameter.hpp new file mode 100644 index 00000000..feb8c6f9 --- /dev/null +++ b/src/defaultParameter.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DEFAULTPARAMETER_HPP_INCLUDED +#define VMIME_DEFAULTPARAMETER_HPP_INCLUDED + + +#include "parameter.hpp" + + +namespace vmime +{ + + +class defaultParameter : public parameter +{ +protected: + + void copyFrom(const parameter& param); + + virtual void parseValue(const string& buffer, const string::size_type position, const string::size_type end) = 0; + virtual const string generateValue() const = 0; + +private: + + const string quotedValue() const; + + // No need to override these in class that derive from "defaultParameter". + // "defaultParameter" provides a default handling for value parsing/building. + // Instead, you must define two functions: "parseValue" and "generateValue" (see above). + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_DEFAULTPARAMETER_HPP_INCLUDED diff --git a/src/defaultParameterizedHeaderField.cpp b/src/defaultParameterizedHeaderField.cpp new file mode 100644 index 00000000..9ca8d154 --- /dev/null +++ b/src/defaultParameterizedHeaderField.cpp @@ -0,0 +1,62 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "defaultParameterizedHeaderField.hpp" + + +namespace vmime +{ + + +defaultParameterizedHeaderField::defaultParameterizedHeaderField() +{ +} + + +void defaultParameterizedHeaderField::parseValue(const string& buffer, + const string::size_type position, const string::size_type end) +{ + m_value = string(buffer.begin() + position, buffer.begin() + end); +} + + +const string defaultParameterizedHeaderField::generateValue() const +{ + return (m_value); +} + + +void defaultParameterizedHeaderField::copyFrom(const headerField& field) +{ + const defaultParameterizedHeaderField& source = dynamic_cast + (field); + m_value = source.m_value; + + parameterizedHeaderField::copyFrom(field); +} + + +defaultParameterizedHeaderField& defaultParameterizedHeaderField::operator=(const string& value) +{ + m_value = value; + return (*this); +} + + +} // vmime diff --git a/src/defaultParameterizedHeaderField.hpp b/src/defaultParameterizedHeaderField.hpp new file mode 100644 index 00000000..d85fde59 --- /dev/null +++ b/src/defaultParameterizedHeaderField.hpp @@ -0,0 +1,63 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DEFAULTPARAMETERIZEDHEADERFIELD_HPP_INCLUDED +#define VMIME_DEFAULTPARAMETERIZEDHEADERFIELD_HPP_INCLUDED + + +#include "parameterizedHeaderField.hpp" +#include "base.hpp" + + +namespace vmime +{ + + +class defaultParameterizedHeaderField : public parameterizedHeaderField +{ + friend class headerFieldFactory::registerer ; + +protected: + + defaultParameterizedHeaderField(); + +public: + + void copyFrom(const headerField& field); + + defaultParameterizedHeaderField& operator=(const string& value); + + const string& value() const { return (m_value); } + string& value() { return (m_value); } + +protected: + + string m_value; + +protected: + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_DEFAULTPARAMETERIZEDHEADERFIELD_HPP_INCLUDED diff --git a/src/disposition.cpp b/src/disposition.cpp new file mode 100644 index 00000000..7f8962f0 --- /dev/null +++ b/src/disposition.cpp @@ -0,0 +1,91 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "disposition.hpp" + + +namespace vmime +{ + + +disposition::disposition() + : m_name(dispositionTypes::INLINE) +{ +} + + +disposition::disposition(const string& name) + : m_name(toLower(name)) +{ +} + + +disposition::disposition(const disposition& type) + : component(), m_name(type.m_name) +{ +} + + +void disposition::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_name = toLower(string(buffer.begin() + position, buffer.begin() + end)); + + if (newPosition) + *newPosition = end; +} + + +void disposition::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + os << m_name; + + if (newLinePos) + *newLinePos = curLinePos + m_name.length(); +} + + +disposition& disposition::operator=(const disposition& source) +{ + m_name = source.m_name; + return (*this); +} + + +disposition& disposition::operator=(const string& name) +{ + m_name = toLower(name); + return (*this); +} + + +const bool disposition::operator==(const disposition& value) const +{ + return (toLower(m_name) == value.m_name); +} + + +const bool disposition::operator!=(const disposition& value) const +{ + return !(*this == value); +} + + +} // vmime diff --git a/src/disposition.hpp b/src/disposition.hpp new file mode 100644 index 00000000..c994a723 --- /dev/null +++ b/src/disposition.hpp @@ -0,0 +1,74 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_DISPOSITION_HPP_INCLUDED +#define VMIME_DISPOSITION_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + + +namespace vmime +{ + + +/** Content disposition (basic type). + */ + +class disposition : public component +{ +public: + + disposition(); + disposition(const string& name); + disposition(const disposition& disp); + +public: + + const string& name() const { return (m_name); } + string& name() { return (m_name); } + +public: + + disposition& operator=(const disposition& source); + disposition& operator=(const string& name); + + const bool operator==(const disposition& value) const; + const bool operator!=(const disposition& value) const; + +protected: + + string m_name; + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_DISPOSITION_HPP_INCLUDED diff --git a/src/encoder.cpp b/src/encoder.cpp new file mode 100644 index 00000000..6634f7bb --- /dev/null +++ b/src/encoder.cpp @@ -0,0 +1,69 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoder.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +encoder::encoder() +{ +} + + +encoder::~encoder() +{ +} + + +const propertySet& encoder::properties() const +{ + return (m_props); +} + + +propertySet& encoder::properties() +{ + return (m_props); +} + + +const propertySet& encoder::results() const +{ + return (m_results); +} + + +propertySet& encoder::results() +{ + return (m_results); +} + + +const std::vector encoder::availableProperties() const +{ + std::vector list; + return (list); +} + + +} // vmime diff --git a/src/encoder.hpp b/src/encoder.hpp new file mode 100644 index 00000000..1aa97d49 --- /dev/null +++ b/src/encoder.hpp @@ -0,0 +1,95 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODER_HPP_INCLUDED +#define VMIME_ENCODER_HPP_INCLUDED + + +#include "base.hpp" +#include "propertySet.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +class encoder +{ +public: + + encoder(); + virtual ~encoder(); + + /** Encode data. + * + * @param in input data (decoded) + * @param out output stream for encoded data + * @return number of bytes written into output stream + */ + virtual const utility::stream::size_type encode(utility::inputStream& in, utility::outputStream& out) = 0; + + /** Decode data. + * + * @param in input data (encoded) + * @param out output stream for decoded data + * @return number of bytes written into output stream + */ + virtual const utility::stream::size_type decode(utility::inputStream& in, utility::outputStream& out) = 0; + + /** Return the properties of the encoder. + * + * @return properties of the encoder + */ + const propertySet& properties() const; + + /** Return the properties of the encoder. + * + * @return properties of the encoder + */ + propertySet& properties(); + + /** Return a list of property names that can be set for + * this encoder. + * + * @return list of property names + */ + virtual const std::vector availableProperties() const; + + /** Return the results returned by this encoder. + * + * @return results returned by the encoder + */ + const propertySet& results() const; + +protected: + + propertySet& results(); + +private: + + propertySet m_props; + propertySet m_results; +}; + + +} // vmime + + +#endif // VMIME_ENCODER_HPP_INCLUDED diff --git a/src/encoder7bit.cpp b/src/encoder7bit.cpp new file mode 100644 index 00000000..b5ae3820 --- /dev/null +++ b/src/encoder7bit.cpp @@ -0,0 +1,32 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoder7bit.hpp" + + +namespace vmime +{ + + +encoder7bit::encoder7bit() +{ +} + + +} // vmime diff --git a/src/encoder7bit.hpp b/src/encoder7bit.hpp new file mode 100644 index 00000000..a4b64cdf --- /dev/null +++ b/src/encoder7bit.hpp @@ -0,0 +1,45 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODER7BIT_HPP_INCLUDED +#define VMIME_ENCODER7BIT_HPP_INCLUDED + + +#include "encoderDefault.hpp" + + +namespace vmime +{ + + +/** 7-bit encoder. + */ + +class encoder7bit : public encoderDefault +{ +public: + + encoder7bit(); +}; + + +} // vmime + + +#endif // VMIME_ENCODER7BIT_HPP_INCLUDED diff --git a/src/encoder8bit.cpp b/src/encoder8bit.cpp new file mode 100644 index 00000000..b9b91457 --- /dev/null +++ b/src/encoder8bit.cpp @@ -0,0 +1,32 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoder8bit.hpp" + + +namespace vmime +{ + + +encoder8bit::encoder8bit() +{ +} + + +} // vmime diff --git a/src/encoder8bit.hpp b/src/encoder8bit.hpp new file mode 100644 index 00000000..7cbbd417 --- /dev/null +++ b/src/encoder8bit.hpp @@ -0,0 +1,45 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODER8BIT_HPP_INCLUDED +#define VMIME_ENCODER8BIT_HPP_INCLUDED + + +#include "encoderDefault.hpp" + + +namespace vmime +{ + + +/** 8-bit encoder. + */ + +class encoder8bit : public encoderDefault +{ +public: + + encoder8bit(); +}; + + +} // vmime + + +#endif // VMIME_ENCODER8BIT_HPP_INCLUDED diff --git a/src/encoderB64.cpp b/src/encoderB64.cpp new file mode 100644 index 00000000..25b15ad9 --- /dev/null +++ b/src/encoderB64.cpp @@ -0,0 +1,265 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoderB64.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +encoderB64::encoderB64() +{ +} + + +const std::vector encoderB64::availableProperties() const +{ + std::vector list(encoder::availableProperties()); + + list.push_back("maxlinelength"); + + return (list); +} + + +// 7-bits alphabet used to encode binary data +const unsigned char encoderB64::sm_alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +const unsigned char encoderB64::sm_decodeMap[256] = +{ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0x00 - 0x0f + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0x10 - 0x1f + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3e,0xff,0xff,0xff,0x3f, // 0x20 - 0x2f + 0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0xff,0xff,0xff,0x3d,0xff,0xff, // 0x30 - 0x3f + 0xff,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e, // 0x40 - 0x4f + 0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0xff,0xff,0xff,0xff,0xff, // 0x50 - 0x5f + 0xff,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28, // 0x60 - 0x6f + 0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0xff,0xff,0xff,0xff, // 0x70 - 0x7f + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0x80 - 0x8f + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0x90 - 0x9f + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0xa0 - 0xaf + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0xb0 - 0xbf + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0xc0 - 0xcf + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0xd0 - 0xdf + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0xe0 - 0xef + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, // 0xf0 - 0xff +}; + + + +const utility::stream::size_type encoderB64::encode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + const int propMaxLineLength = properties().get ("maxlinelength", -1); + + const bool cutLines = (propMaxLineLength != -1); + const int maxLineLength = std::min(propMaxLineLength, 76); + + // Process data + utility::stream::value_type buffer[65536]; + utility::stream::size_type bufferLength = 0; + utility::stream::size_type bufferPos = 0; + + unsigned char bytes[3]; + unsigned char output[4]; + + utility::stream::size_type total = 0; + + int curCol = 0; + + while (bufferPos < bufferLength || !in.eof()) + { + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + + if (bufferLength == 0) + break; + } + + // Get 3 bytes of data + int count = 0; + + while (count < 3 && bufferPos < bufferLength) + bytes[count++] = buffer[bufferPos++]; + + if (count != 3) + { + // There may be more data in the next chunk... + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + } + + while (count < 3 && bufferPos < bufferLength) + bytes[count++] = buffer[bufferPos++]; + } + + // Encode data + switch (count) + { + case 1: + + output[0] = sm_alphabet[(bytes[0] & 0xFC) >> 2]; + output[1] = sm_alphabet[(bytes[0] & 0x03) << 4]; + output[2] = sm_alphabet[64]; // padding + output[3] = sm_alphabet[64]; // padding + + break; + + case 2: + + output[0] = sm_alphabet[(bytes[0] & 0xFC) >> 2]; + output[1] = sm_alphabet[((bytes[0] & 0x03) << 4) | ((bytes[1] & 0xF0) >> 4)]; + output[2] = sm_alphabet[(bytes[1] & 0x0F) << 2]; + output[3] = sm_alphabet[64]; // padding + + break; + + default: + case 3: + + output[0] = sm_alphabet[(bytes[0] & 0xFC) >> 2]; + output[1] = sm_alphabet[((bytes[0] & 0x03) << 4) | ((bytes[1] & 0xF0) >> 4)]; + output[2] = sm_alphabet[((bytes[1] & 0x0F) << 2) | ((bytes[2] & 0xC0) >> 6)]; + output[3] = sm_alphabet[(bytes[2] & 0x3F)]; + + break; + } + + // Write encoded data to output stream + out.write((char*) output, 4); + + total += 4; + curCol += 4; + + if (cutLines && curCol >= maxLineLength - 1) + { + out.write("\r\n", 2); + curCol = 0; + } + } + + return (total); +} + + +const utility::stream::size_type encoderB64::decode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + // Process the data + char buffer[16384]; + int bufferLength = 0; + int bufferPos = 0; + + utility::stream::size_type total = 0; + + unsigned char bytes[4]; + unsigned char output[3]; + + while (bufferPos < bufferLength || !in.eof()) + { + // Need to get more data? + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + + // No more data + if (bufferLength == 0) + break; + } + + // 4 bytes of input provide 3 bytes of output, so + // get the next 4 bytes from the input stream. + int count = 0; + + while (count < 4 && bufferPos < bufferLength) + { + const unsigned char c = buffer[bufferPos++]; + + if (!isspace(c)) + bytes[count++] = c; + } + + if (count != 4) + { + while (count < 4 && !in.eof()) + { + // Data continues on the next chunk + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + + while (count < 4 && bufferPos < bufferLength) + { + const unsigned char c = buffer[bufferPos++]; + + if (!isspace(c)) + bytes[count++] = c; + } + } + } + + // Decode the bytes + unsigned char c1 = bytes[0]; + unsigned char c2 = bytes[1]; + + if (c1 == '=' || c2 == '=') // end + break; + + output[0] = (unsigned char)((sm_decodeMap[c1] << 2) | ((sm_decodeMap[c2] & 0x30) >> 4)); + + c1 = bytes[2]; + + if (c1 == '=') // end + { + out.write((char*) output, 1); + total += 1; + break; + } + + output[1] = (unsigned char)(((sm_decodeMap[c2] & 0xf) << 4) | ((sm_decodeMap[c1] & 0x3c) >> 2)); + + c2 = bytes[3]; + + if (c2 == '=') // end + { + out.write((char*) output, 2); + total += 2; + break; + } + + output[2] = (unsigned char)(((sm_decodeMap[c1] & 0x03) << 6) | sm_decodeMap[c2]); + + out.write((char*) output, 3); + total += 3; + } + + return (total); +} + + +} // vmime diff --git a/src/encoderB64.hpp b/src/encoderB64.hpp new file mode 100644 index 00000000..ff978985 --- /dev/null +++ b/src/encoderB64.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODERB64_HPP_INCLUDED +#define VMIME_ENCODERB64_HPP_INCLUDED + + +#include "encoder.hpp" + + +namespace vmime +{ + + +/** Base64 encoder. + */ + +class encoderB64 : public encoder +{ +public: + + encoderB64(); + + const utility::stream::size_type encode(utility::inputStream& in, utility::outputStream& out); + const utility::stream::size_type decode(utility::inputStream& in, utility::outputStream& out); + + const std::vector availableProperties() const; + +protected: + + static const unsigned char sm_alphabet[]; + static const unsigned char sm_decodeMap[256]; +}; + + +} // vmime + + +#endif // VMIME_ENCODERB64_HPP_INCLUDED diff --git a/src/encoderBinary.cpp b/src/encoderBinary.cpp new file mode 100644 index 00000000..606da99c --- /dev/null +++ b/src/encoderBinary.cpp @@ -0,0 +1,32 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoderBinary.hpp" + + +namespace vmime +{ + + +encoderBinary::encoderBinary() +{ +} + + +} // vmime diff --git a/src/encoderBinary.hpp b/src/encoderBinary.hpp new file mode 100644 index 00000000..dadca217 --- /dev/null +++ b/src/encoderBinary.hpp @@ -0,0 +1,45 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODERBINARY_HPP_INCLUDED +#define VMIME_ENCODERBINARY_HPP_INCLUDED + + +#include "encoderDefault.hpp" + + +namespace vmime +{ + + +/** Binary encoder. + */ + +class encoderBinary : public encoderDefault +{ +public: + + encoderBinary(); +}; + + +} // vmime + + +#endif // VMIME_ENCODERBINARY_HPP_INCLUDED diff --git a/src/encoderDefault.cpp b/src/encoderDefault.cpp new file mode 100644 index 00000000..cdff3735 --- /dev/null +++ b/src/encoderDefault.cpp @@ -0,0 +1,50 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoderDefault.hpp" + + +namespace vmime +{ + + +encoderDefault::encoderDefault() +{ +} + + +const utility::stream::size_type encoderDefault::encode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + // No encoding performed + return (utility::bufferedStreamCopy(in, out)); +} + + +const utility::stream::size_type encoderDefault::decode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + // No decoding performed + return (utility::bufferedStreamCopy(in, out)); +} + + +} // vmime diff --git a/src/encoderDefault.hpp b/src/encoderDefault.hpp new file mode 100644 index 00000000..d54d1f0f --- /dev/null +++ b/src/encoderDefault.hpp @@ -0,0 +1,48 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODERDEFAULT_HPP_INCLUDED +#define VMIME_ENCODERDEFAULT_HPP_INCLUDED + + +#include "encoder.hpp" + + +namespace vmime +{ + + +/** Default encoder (simple copy, no encoding/decoding is performed). + */ + +class encoderDefault : public encoder +{ +public: + + encoderDefault(); + + const utility::stream::size_type encode(utility::inputStream& in, utility::outputStream& out); + const utility::stream::size_type decode(utility::inputStream& in, utility::outputStream& out); +}; + + +} // vmime + + +#endif // VMIME_ENCODERDEFAUL_HPP_INCLUDED diff --git a/src/encoderFactory.cpp b/src/encoderFactory.cpp new file mode 100644 index 00000000..6da09dbb --- /dev/null +++ b/src/encoderFactory.cpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoderFactory.hpp" +#include "exception.hpp" + +#include "encoderB64.hpp" +#include "encoderQP.hpp" +#include "encoderUUE.hpp" +#include "encoderBinary.hpp" +#include "encoder7bit.hpp" +#include "encoder8bit.hpp" + + +namespace vmime +{ + + +encoderFactory::encoderFactory() +{ + // Register some default encoders + registerName ("base64"); + registerName ("quoted-printable"); + registerName ("uuencode"); + registerName ("7bit"); + registerName ("8bit"); + registerName ("binary"); +} + + +encoderFactory::~encoderFactory() +{ + for (NameMap::iterator it = m_nameMap.begin() ; it != m_nameMap.end() ; ++it) + delete ((*it).second); +} + + +encoder* encoderFactory::create(const string& name) +{ + NameMap::const_iterator pos = m_nameMap.find(toLower(name)); + + if (pos != m_nameMap.end()) + { + return ((*pos).second)->create(); + } + else + { + throw exceptions::no_encoder_available(); + return (NULL); + } +} + + +} // vmime diff --git a/src/encoderFactory.hpp b/src/encoderFactory.hpp new file mode 100644 index 00000000..cbd52048 --- /dev/null +++ b/src/encoderFactory.hpp @@ -0,0 +1,183 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODERFACTORY_HPP_INCLUDED +#define VMIME_ENCODERFACTORY_HPP_INCLUDED + + +#include "encoder.hpp" +#include "utility/singleton.hpp" + + +namespace vmime +{ + + +/** A factory to create 'encoder' objects for the specified encoding. + */ + +class encoderFactory : public utility::singleton +{ + friend class utility::singleton ; + +private: + + encoderFactory(); + ~encoderFactory(); + +public: + + class registeredEncoder + { + friend class encoderFactory; + + protected: + + virtual ~registeredEncoder() { } + + public: + + virtual encoder* create() = 0; + + virtual const string& name() const = 0; + }; + +private: + + template + class registeredEncoderImpl : public registeredEncoder + { + friend class encoderFactory; + + protected: + + registeredEncoderImpl(const string& name) : m_name(name) { } + + public: + + encoder* create() + { + return new E; + } + + const string& name() const + { + return (m_name); + } + + private: + + const string m_name; + }; + + + typedef std::map NameMap; + NameMap m_nameMap; + +public: + + template + void registerName(const string& name) + { + const string _name = toLower(name); + m_nameMap.insert(NameMap::value_type(_name, + new registeredEncoderImpl (_name))); + } + + encoder* create(const string& name); + + const registeredEncoder& operator[](const string& name) const; + + + class iterator; + + class const_iterator + { + friend class encoderFactory; + + public: + + const_iterator() { } + const_iterator(const const_iterator& it) : m_it(it.m_it) { } + const_iterator(const iterator& it) : m_it(it.m_it) { } + + const_iterator& operator=(const const_iterator& it) { m_it = it.m_it; return (*this); } + + const registeredEncoder& operator*() const { return (*(*m_it).second); } + const registeredEncoder* operator->() const { return ((*m_it).second); } + + const_iterator& operator++() { ++m_it; return (*this); } + const_iterator operator++(int) { return (m_it++); } + + const_iterator& operator--() { --m_it; return (*this); } + const_iterator operator--(int) { return (m_it--); } + + const bool operator==(const const_iterator& it) const { return (m_it == it.m_it); } + const bool operator!=(const const_iterator& it) const { return (m_it != it.m_it); } + + private: + + const_iterator(const NameMap::const_iterator it) : m_it(it) { } + + NameMap::const_iterator m_it; + }; + + class iterator + { + friend class encoderFactory; + friend class encoderFactory::const_iterator; + + public: + + iterator() { } + iterator(const iterator& it) : m_it(it.m_it) { } + + iterator& operator=(const iterator& it) { m_it = it.m_it; return (*this); } + + registeredEncoder& operator*() const { return (*(*m_it).second); } + registeredEncoder* operator->() const { return ((*m_it).second); } + + iterator& operator++() { ++m_it; return (*this); } + iterator operator++(int) { return (m_it++); } + + iterator& operator--() { --m_it; return (*this); } + iterator operator--(int) { return (m_it--); } + + const bool operator==(const iterator& it) const { return (m_it == it.m_it); } + const bool operator!=(const iterator& it) const { return (m_it != it.m_it); } + + private: + + iterator(const NameMap::iterator it) : m_it(it) { } + + NameMap::iterator m_it; + }; + + iterator begin() { return iterator(m_nameMap.begin()); } + iterator end() { return iterator(m_nameMap.end()); } + + const_iterator begin() const { return const_iterator(m_nameMap.begin()); } + const_iterator end() const { return const_iterator(m_nameMap.end()); } +}; + + +} // vmime + + +#endif // VMIME_ENCODERFACTORY_HPP_INCLUDED diff --git a/src/encoderQP.cpp b/src/encoderQP.cpp new file mode 100644 index 00000000..46125c93 --- /dev/null +++ b/src/encoderQP.cpp @@ -0,0 +1,422 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoderQP.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +encoderQP::encoderQP() +{ +} + + +const std::vector encoderQP::availableProperties() const +{ + std::vector list(encoder::availableProperties()); + + list.push_back("maxlinelength"); + + list.push_back("text"); // if set, '\r' and '\n' are not hex-encoded. + // WARNING! You should not use this for binary data! + + list.push_back("rfc2047"); // for header fields encoding (RFC #2047) + + return (list); +} + + + +// Encoding table +const unsigned char encoderQP::sm_hexDigits[] = "0123456789ABCDEF"; + +// Decoding table +const unsigned char encoderQP::sm_hexDecodeTable[256] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +#define QP_ENCODE_HEX(x) \ + outBuffer[outBufferPos] = '='; \ + outBuffer[outBufferPos + 1] = sm_hexDigits[x >> 4]; \ + outBuffer[outBufferPos + 2] = sm_hexDigits[x & 0xF]; \ + outBufferPos += 3; \ + curCol += 3; + + +const utility::stream::size_type encoderQP::encode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + const string::size_type propMaxLineLength = + properties().get ("maxlinelength", (string::size_type) -1); + + const bool rfc2047 = properties().get ("rfc2047", false); + const bool text = properties().get ("text", false); // binary mode by default + + const bool cutLines = (propMaxLineLength != (string::size_type) -1); + const string::size_type maxLineLength = std::min(propMaxLineLength, (string::size_type) 74); + + // Process the data + char buffer[16384]; + int bufferLength = 0; + int bufferPos = 0; + + string::size_type curCol = 0; + + unsigned char outBuffer[16384]; + int outBufferPos = 0; + + utility::stream::size_type total = 0; + + while (bufferPos < bufferLength || !in.eof()) + { + // Flush current output buffer + if (outBufferPos + 6 >= (int) sizeof(outBuffer)) + { + out.write((char*) outBuffer, outBufferPos); + + total += outBufferPos; + outBufferPos = 0; + } + + // Need to get more data? + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + + // No more data + if (bufferLength == 0) + break; + } + + // Get the next char and encode it + const unsigned char c = (unsigned char) buffer[bufferPos++]; + + switch (c) + { + case '.': + { + if (!rfc2047 && curCol == 0) + { + // If a '.' appears at the beginning of a line, we encode it to + // to avoid problems with SMTP servers... ("\r\n.\r\n" means the + // end of data transmission). + QP_ENCODE_HEX('.') + continue; + } + + outBuffer[outBufferPos++] = '.'; + ++curCol; + break; + } + case ' ': + { + // RFC-2047, Page 5, 4.2. The "Q" encoding: + // << The 8-bit hexadecimal value 20 (e.g., ISO-8859-1 SPACE) may be + // represented as "_" (underscore, ASCII 95.). >> + if (rfc2047) + { + outBuffer[outBufferPos++] = ' '; + ++curCol; + } + else + { + // Need to get more data? + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + } + + // Spaces cannot appear at the end of a line. So, encode the space. + if (bufferPos >= bufferLength || + (buffer[bufferPos] == '\r' || buffer[bufferPos] == '\n')) + { + QP_ENCODE_HEX(' '); + } + else + { + outBuffer[outBufferPos++] = ' '; + ++curCol; + } + } + + break; + } + case '\t': + { + QP_ENCODE_HEX(c) + break; + } + case '\r': + case '\n': + { + // Text mode (where using CRLF or LF or ... does not + // care for a new line...) + if (text) + { + outBuffer[outBufferPos++] = c; + ++curCol; + } + // Binary mode (where CR and LF bytes are important!) + else + { + QP_ENCODE_HEX(c) + } + + break; + } + case '=': + { + QP_ENCODE_HEX('=') + break; + } + case ',': + case ';': + case ':': + case '_': + { + if (rfc2047) + { + QP_ENCODE_HEX(c) + } + else + { + outBuffer[outBufferPos++] = c; + ++curCol; + } + + break; + } + /* + Rule #2: (Literal representation) Octets with decimal values of 33 + through 60 inclusive, and 62 through 126, inclusive, MAY be + represented as the ASCII characters which correspond to those + octets (EXCLAMATION POINT through LESS THAN, and GREATER THAN + through TILDE, respectively). + */ + default: + { + //if ((c >= 33 && c <= 60) || (c >= 62 && c <= 126)) + if (c >= 33 && c <= 126 && c != 61) + { + outBuffer[outBufferPos++] = c; + ++curCol; + } + // Other characters: '=' + hexadecimal encoding + else + { + QP_ENCODE_HEX(c) + } + + break; + } + + } + + // Soft line break : "=\r\n" + if (cutLines && curCol >= maxLineLength - 1) + { + outBuffer[outBufferPos] = '='; + outBuffer[outBufferPos + 1] = '\r'; + outBuffer[outBufferPos + 2] = '\n'; + + outBufferPos += 3; + curCol = 0; + } + } + + // Flush remaining output buffer + if (outBufferPos != 0) + { + out.write((char*) outBuffer, outBufferPos); + total += outBufferPos; + } + + return (total); +} + + +const utility::stream::size_type encoderQP::decode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + // Process the data + const bool rfc2047 = properties().get ("rfc2047", false); + + char buffer[16384]; + int bufferLength = 0; + int bufferPos = 0; + + unsigned char outBuffer[16384]; + int outBufferPos = 0; + + utility::stream::size_type total = 0; + + while (bufferPos < bufferLength || !in.eof()) + { + // Flush current output buffer + if (outBufferPos >= (int) sizeof(outBuffer)) + { + out.write((char*) outBuffer, outBufferPos); + + total += outBufferPos; + outBufferPos = 0; + } + + // Need to get more data? + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + + // No more data + if (bufferLength == 0) + break; + } + + // Decode the next sequence (hex-encoded byte or printable character) + unsigned char c = (unsigned char) buffer[bufferPos++]; + + switch (c) + { + case '=': + { + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + } + + if (bufferPos < bufferLength) + { + c = (unsigned char) buffer[bufferPos++]; + + switch (c) + { + // Ignore soft line break ("=\r\n" or "=\n") + case '\r': + + // Read one byte more + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + } + + if (bufferPos < bufferLength) + ++bufferPos; + + break; + + case '\n': + + break; + + // Hex-encoded char + default: + { + // We need another byte... + if (bufferPos >= bufferLength) + { + bufferLength = in.read(buffer, sizeof(buffer)); + bufferPos = 0; + } + + if (bufferPos < bufferLength) + { + const unsigned char next = (unsigned char) buffer[bufferPos++]; + + const unsigned char value = + sm_hexDecodeTable[c] * 16 + + sm_hexDecodeTable[next]; + + outBuffer[outBufferPos++] = value; + } + else + { + // Premature end-of-data + } + + break; + } + + } + } + else + { + // Premature end-of-data + } + + break; + } + case '_': + { + if (rfc2047) + { + // RFC-2047, Page 5, 4.2. The "Q" encoding: + // << Note that the "_" always represents hexadecimal 20, even if the SPACE + // character occupies a different code position in the character set in use. >> + outBuffer[outBufferPos++] = 0x20; + break; + } + + // no break here... + } + default: + { + outBuffer[outBufferPos++] = c; + } + + } + } + + // Flush remaining output buffer + if (outBufferPos != 0) + { + out.write((char*) outBuffer, outBufferPos); + total += outBufferPos; + } + + return (total); +} + + +} // vmime diff --git a/src/encoderQP.hpp b/src/encoderQP.hpp new file mode 100644 index 00000000..519f43f1 --- /dev/null +++ b/src/encoderQP.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODERQP_HPP_INCLUDED +#define VMIME_ENCODERQP_HPP_INCLUDED + + +#include "encoder.hpp" + + +namespace vmime +{ + + +/** Quoted-printable encoder. + */ + +class encoderQP : public encoder +{ +public: + + encoderQP(); + + const utility::stream::size_type encode(utility::inputStream& in, utility::outputStream& out); + const utility::stream::size_type decode(utility::inputStream& in, utility::outputStream& out); + + const std::vector availableProperties() const; + +protected: + + static const unsigned char sm_hexDigits[17]; + static const unsigned char sm_hexDecodeTable[256]; +}; + + +} // vmime + + +#endif // VMIME_ENCODERQP_HPP_INCLUDED diff --git a/src/encoderUUE.cpp b/src/encoderUUE.cpp new file mode 100644 index 00000000..78dffa0c --- /dev/null +++ b/src/encoderUUE.cpp @@ -0,0 +1,289 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoderUUE.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +encoderUUE::encoderUUE() +{ + properties()["mode"] = 644; + properties()["filename"] = "no_name"; + properties()["maxlinelength"] = 46; +} + + +const std::vector encoderUUE::availableProperties() const +{ + std::vector list(encoder::availableProperties()); + + list.push_back("maxlinelength"); + + list.push_back("mode"); + list.push_back("filename"); + + return (list); +} + + +// This is the character encoding function to make a character printable +static inline const unsigned char UUENCODE(const unsigned char c) +{ + return ((c & 077) + ' '); +} + +// Single character decoding +static inline const unsigned char UUDECODE(const unsigned char c) +{ + return ((c - ' ') & 077); +} + + +const utility::stream::size_type encoderUUE::encode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + const string propFilename = properties().get ("filename", ""); + const string propMode = properties().get ("mode", "644"); + + const string::size_type maxLineLength = + std::min(properties().get ("maxlinelength", 46), + static_cast (46)); + + utility::stream::size_type total = 0; + + // Output the prelude text ("begin [mode] [filename]") + out << "begin"; + + if (!propFilename.empty()) + { + out << " " << propMode << " " << propFilename; + total += 2 + propMode.length() + propFilename.length(); + } + + out << "\r\n"; + total += 7; + + // Process the data + utility::stream::value_type inBuffer[64]; + utility::stream::value_type outBuffer[64]; + + while (!in.eof()) + { + // Process up to 45 characters per line + std::fill(inBuffer, inBuffer + sizeof(inBuffer), 0); + + const utility::stream::size_type inLength = in.read(inBuffer, maxLineLength - 1); + + outBuffer[0] = UUENCODE(inLength); // Line length + + utility::stream::size_type j = 1; + + for (utility::stream::size_type i = 0 ; i < inLength ; i += 3, j += 4) + { + const unsigned char c1 = (unsigned char) inBuffer[i]; + const unsigned char c2 = (unsigned char) inBuffer[i + 1]; + const unsigned char c3 = (unsigned char) inBuffer[i + 2]; + + outBuffer[j] = UUENCODE(c1 >> 2); + outBuffer[j + 1] = UUENCODE((c1 << 4) & 060 | (c2 >> 4) & 017); + outBuffer[j + 2] = UUENCODE((c2 << 2) & 074 | (c3 >> 6) & 03); + outBuffer[j + 3] = UUENCODE(c3 & 077); + } + + outBuffer[j] = '\r'; + outBuffer[j + 1] = '\n'; + + out.write(outBuffer, j + 2); + + total += j + 2; + } + + out << "end\r\n"; + total += 5; + + return (total); +} + + +const utility::stream::size_type encoderUUE::decode(utility::inputStream& in, utility::outputStream& out) +{ + in.reset(); // may not work... + + // Process the data + utility::stream::value_type inBuffer[64]; + utility::stream::value_type outBuffer[64]; + + utility::stream::size_type total = 0; + + bool stop = false; + + std::fill(inBuffer, inBuffer + sizeof(inBuffer), 0); + + while (!stop && !in.eof()) + { + // Get the line length + utility::stream::value_type lengthChar; + + if (in.read(&lengthChar, 1) == 0) + break; + + const utility::stream::size_type outLength = UUDECODE(lengthChar); + const utility::stream::size_type inLength = std::min((outLength * 4) / 3, (utility::stream::size_type) 64); + utility::stream::value_type inPos = 0; + + switch (lengthChar) + { + case ' ': + case '\t': + case '\r': + case '\n': + { + // Ignore + continue; + } + case 'b': + { + // Read 5 characters more to check for begin ("begin ...\r\n" or "begin ...\n") + inPos = in.read(inBuffer, 5); + + if (inPos == 5 && + inBuffer[0] == 'e' && + inBuffer[1] == 'g' && + inBuffer[2] == 'i' && + inBuffer[3] == 'n' && + isspace(inBuffer[4])) + { + utility::stream::value_type c = 0; + + utility::stream::size_type count = 0; + utility::stream::value_type buffer[512]; + + while (count < sizeof(buffer) - 1 && in.read(&c, 1) == 1) + { + if (c == '\n') + break; + + buffer[count++] = c; + } + + if (c != '\n') + { + // OOPS! Weird line. Don't try to decode more... + return (total); + } + + // Parse filename and mode + if (count > 0) + { + buffer[count] = '\0'; + + utility::stream::value_type* p = buffer; + + while (*p && isspace(*p)) ++p; + + utility::stream::value_type* modeStart = buffer; + + while (*p && !isspace(*p)) ++p; + + results()["mode"] = string(modeStart, p); + + while (*p && isspace(*p)) ++p; + + utility::stream::value_type* filenameStart = buffer; + + while (*p && !(*p == '\r' || *p == '\n')) ++p; + + results()["filename"] = string(filenameStart, p); + } + // No filename or mode specified + else + { + results()["filename"] = "untitled"; + results()["mode"] = 644; + } + + continue; + } + + break; + } + case 'e': + { + // Read 3 characters more to check for end ("end\r\n" or "end\n") + inPos = in.read(inBuffer, 3); + + if (inPos == 3 && + inBuffer[0] == 'n' && + inBuffer[1] == 'd' && + (inBuffer[2] == '\r' || inBuffer[2] == '\n')) + { + stop = true; + continue; + } + + break; + } + + } + + // Read encoded data + if (in.read(inBuffer + inPos, inLength - inPos) != inLength - inPos) + { + // Premature end of data + break; + } + + // Decode data + for (utility::stream::size_type i = 0, j = 0 ; i < inLength ; i += 4, j += 3) + { + const unsigned char c1 = (unsigned char) inBuffer[i]; + const unsigned char c2 = (unsigned char) inBuffer[i + 1]; + const unsigned char c3 = (unsigned char) inBuffer[i + 2]; + const unsigned char c4 = (unsigned char) inBuffer[i + 3]; + + const utility::stream::size_type n = + std::min(inLength - i, static_cast (3)); + + switch (n) + { + default: + case 3: outBuffer[j + 2] = UUDECODE(c3) << 6 | UUDECODE(c4); + case 2: outBuffer[j + 1] = UUDECODE(c2) << 4 | UUDECODE(c3) >> 2; + case 1: outBuffer[j] = UUDECODE(c1) << 2 | UUDECODE(c2) >> 4; + case 0: break; + } + + total += n; + } + + out.write(outBuffer, outLength); + + std::fill(inBuffer, inBuffer + sizeof(inBuffer), 0); + } + + return (total); +} + + +} // vmime diff --git a/src/encoderUUE.hpp b/src/encoderUUE.hpp new file mode 100644 index 00000000..c7784474 --- /dev/null +++ b/src/encoderUUE.hpp @@ -0,0 +1,50 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODERUUE_HPP_INCLUDED +#define VMIME_ENCODERUUE_HPP_INCLUDED + + +#include "encoder.hpp" + + +namespace vmime +{ + + +/** UUEncode encoder. + */ + +class encoderUUE : public encoder +{ +public: + + encoderUUE(); + + const utility::stream::size_type encode(utility::inputStream& in, utility::outputStream& out); + const utility::stream::size_type decode(utility::inputStream& in, utility::outputStream& out); + + const std::vector availableProperties() const; +}; + + +} // vmime + + +#endif // VMIME_ENCODERUUE_HPP_INCLUDED diff --git a/src/encoding.cpp b/src/encoding.cpp new file mode 100644 index 00000000..c797cf18 --- /dev/null +++ b/src/encoding.cpp @@ -0,0 +1,161 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "encoding.hpp" +#include "encoderFactory.hpp" +#include "contentHandler.hpp" + +#include + + +namespace vmime +{ + + +encoding::encoding() + : m_name(encodingTypes::SEVEN_BIT) +{ +} + + +encoding::encoding(const string& name) + : m_name(toLower(name)) +{ +} + + +encoding::encoding(const encoding& enc) + : component(), m_name(enc.m_name) +{ +} + + +void encoding::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_name = toLower(string(buffer.begin() + position, buffer.begin() + end)); + + if (newPosition) + *newPosition = end; +} + + +void encoding::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + os << m_name; + + if (newLinePos) + *newLinePos = curLinePos + m_name.length(); +} + + +encoder* encoding::getEncoder() const +{ + return (encoderFactory::getInstance()->create(generate())); +} + + +encoding& encoding::operator=(const encoding& source) +{ + m_name = source.m_name; + return (*this); +} + + +encoding& encoding::operator=(const string& name) +{ + m_name = toLower(name); + return (*this); +} + + +const bool encoding::operator==(const encoding& value) const +{ + return (toLower(m_name) == value.m_name); +} + + +const bool encoding::operator!=(const encoding& value) const +{ + return !(*this == value); +} + + +const encoding encoding::decide + (const string::const_iterator begin, const string::const_iterator end) +{ + const string::difference_type length = end - begin; + const string::difference_type count = std::count_if + (begin, end, std::bind2nd(std::less(), 127)); + + // All is in 7-bit US-ASCII --> 7-bit (or Quoted-Printable...) + if (length == count) + { + // Now, we check if there is any line with more than + // "lineLengthLimits::convenient" characters (7-bit requires that) + string::const_iterator p = begin; + + const string::size_type maxLen = lineLengthLimits::convenient; + string::size_type len = 0; + + for ( ; p != end && len <= maxLen ; ) + { + if (*p == '\n') + { + len = 0; + ++p; + + // May or may not need to be encoded, we don't take + // any risk (avoid problems with SMTP) + if (p != end && *p == '.') + len = maxLen + 1; + } + else + { + ++len; + ++p; + } + } + + if (len > maxLen) + return (encoding(encodingTypes::QUOTED_PRINTABLE)); + else + return (encoding(encodingTypes::SEVEN_BIT)); + } + // Less than 20% non US-ASCII --> Quoted-Printable + else if ((length - count) <= length / 5) + { + return (encoding(encodingTypes::QUOTED_PRINTABLE)); + } + // Otherwise --> Base64 + else + { + return (encoding(encodingTypes::BASE64)); + } +} + + +const encoding encoding::decide(const contentHandler& /* data */) +{ + return (encoding(encodingTypes::BASE64)); +} + + +} // vmime diff --git a/src/encoding.hpp b/src/encoding.hpp new file mode 100644 index 00000000..ca344fff --- /dev/null +++ b/src/encoding.hpp @@ -0,0 +1,87 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_ENCODING_HPP_INCLUDED +#define VMIME_ENCODING_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" +#include "encoder.hpp" + + +namespace vmime +{ + + +class contentHandler; + + +/** Content encoding (basic type). + */ + +class encoding : public component +{ +public: + + encoding(); + encoding(const string& name); + encoding(const encoding& enc); + +public: + + const string& name() const { return (m_name); } + string& name() { return (m_name); } + +public: + + encoding& operator=(const encoding& source); + encoding& operator=(const string& name); + + const bool operator==(const encoding& value) const; + const bool operator!=(const encoding& value) const; + + // Decide which encoding to use based on the data + static const encoding decide(const string::const_iterator begin, const string::const_iterator end); + static const encoding decide(const contentHandler& data); + +public: + + // Obtain an encoder/decoder for the current encoding type + encoder* getEncoder() const; + +protected: + + string m_name; + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_ENCODING_HPP_INCLUDED diff --git a/src/exception.hpp b/src/exception.hpp new file mode 100644 index 00000000..b7880d31 --- /dev/null +++ b/src/exception.hpp @@ -0,0 +1,558 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_EXCEPTION_HPP_INCLUDED +#define VMIME_EXCEPTION_HPP_INCLUDED + + +#include "config.hpp" +#include "base.hpp" +#include "utility/path.hpp" + + +namespace vmime +{ + + +class exception +{ +protected: + + string m_what; + +public: + + exception(const string& what) : m_what(what) { } + virtual ~exception() { } + + const string what() const throw() { return (m_what); }; +}; + + +namespace exceptions +{ + + +class bad_field_type : public vmime::exception +{ +public: + + bad_field_type() : exception("Bad field type.") {} + ~bad_field_type() throw() {} +}; + + +class charset_conv_error : public vmime::exception +{ +public: + + charset_conv_error() : exception("Charset conversion error.") {} + ~charset_conv_error() throw() {} +}; + + +class no_encoder_available : public vmime::exception +{ +public: + + no_encoder_available() : exception("No encoder available.") {} + ~no_encoder_available() throw() {} +}; + + +class no_such_parameter : public vmime::exception +{ +public: + + no_such_parameter(const string& name) : exception + (string("Parameter not found: '") + name + string("'.")) {} + ~no_such_parameter() throw() {} +}; + + +class no_such_field : public vmime::exception +{ +public: + + no_such_field() : exception("Field not found.") {} + ~no_such_field() throw() {} +}; + + +class open_file_error : public vmime::exception +{ +public: + + open_file_error() : exception("Error opening file.") {} + ~open_file_error() throw() {} +}; + + +class no_factory_available : public vmime::exception +{ +public: + + no_factory_available() : exception("No factory available.") {} + ~no_factory_available() throw() {} +}; + + +class no_platform_dependant_handler : public vmime::exception +{ +public: + + no_platform_dependant_handler() : exception("No platform-dependant handler installed.") {} + ~no_platform_dependant_handler() throw() {} +}; + + +class no_expeditor : public vmime::exception +{ +public: + + no_expeditor() : exception("No expeditor specified.") {} + ~no_expeditor() throw() {} +}; + + +class no_recipient : public vmime::exception +{ +public: + + no_recipient() : exception("No recipient specified.") {} + ~no_recipient() throw() {} +}; + + +class no_object_found : public vmime::exception +{ +public: + + no_object_found() : exception("No object found.") {} + ~no_object_found() throw() {} +}; + + +// There is no property with that name in the set. + +class no_such_property : public vmime::exception +{ +public: + + no_such_property(const string& name) : exception + (std::string("No such property: '") + name + string("'.")) { } + ~no_such_property() throw() {} +}; + + +// Bad type specified when reading property. + +class invalid_property_type : public vmime::exception +{ +public: + + invalid_property_type() : exception("Invalid property type.") {} + ~invalid_property_type() throw() {} +}; + + +// Bad argument was passed to the function. + +class invalid_argument : public vmime::exception +{ +public: + + invalid_argument() : exception("Invalid argument.") {} + ~invalid_argument() throw() {} +}; + + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +/** Base class for exceptions thrown by the messaging module. + */ + +class messaging_exception : public vmime::exception +{ +public: + + messaging_exception(const string& what) : exception(what) {} + ~messaging_exception() throw() {} +}; + + +/** Error while connecting to the server: this may be a DNS resolution error + * or a connection error (for example, time-out while connecting). + */ + +class connection_error : public messaging_exception +{ +public: + + connection_error() : messaging_exception("Connection error.") {} + ~connection_error() throw() {} +}; + + +/** Server did not initiated the connection correctly. + */ + +class connection_greeting_error : public messaging_exception +{ +public: + + connection_greeting_error(const string& response) + : messaging_exception("Greeting error."), m_response(response) {} + ~connection_greeting_error() throw() {} + + const string& response() const { return (m_response); } + +private: + + string m_response; +}; + + +/** Error while giving credentials to the server (wrong username + * or password, or wrong authentication method). + */ + +class authentication_error : public messaging_exception +{ +public: + + authentication_error(const string& response) + : messaging_exception("Authentication error."), m_response(response) {} + ~authentication_error() throw() {} + + const string& response() const { return (m_response); } + +private: + + string m_response; +}; + + +/** Option not supported. + */ + +class unsupported_option : public messaging_exception +{ +public: + + unsupported_option() : messaging_exception("Unsupported option.") {} + ~unsupported_option() throw() {} +}; + + +/** No service available for this protocol. + */ + +class no_service_available : public messaging_exception +{ +public: + + no_service_available() : messaging_exception("No service available for this protocol.") {} + ~no_service_available() throw() {} +}; + + +/** The current state of the object does not permit to execute the + * operation (for example, you try to close a folder which is not open). + */ + +class illegal_state : public messaging_exception +{ +public: + + illegal_state(const string& state) + : messaging_exception("Illegal state to accomplish the operation: '" + state + "'.") {} + ~illegal_state() throw() {} +}; + + +/** Folder not found (does not exist). + */ + +class folder_not_found : public messaging_exception +{ +public: + + folder_not_found() : messaging_exception("Folder not found.") {} + ~folder_not_found() throw() {} +}; + + +/** Message not found (does not exist). + */ + +class message_not_found : public messaging_exception +{ +public: + + message_not_found() : messaging_exception("Message not found.") {} + ~message_not_found() throw() {} +}; + + +/** Operation not supported by the underlying protocol. + */ + +class operation_not_supported : public messaging_exception +{ +public: + + operation_not_supported() : messaging_exception("Operation not supported.") {} + ~operation_not_supported() throw() {} +}; + + +/** The operation timed out (time-out delay is elapsed). + */ + +class operation_timed_out : public messaging_exception +{ +public: + + operation_timed_out() : messaging_exception("Operation timed out.") {} + ~operation_timed_out() throw() {} +}; + + +/** The operation has been cancelled. + */ + +class operation_cancelled : public messaging_exception +{ +public: + + operation_cancelled() : messaging_exception("Operation cancelled by the user.") {} + ~operation_cancelled() throw() {} +}; + + +/** Must call fetchMessage() or fetchHeader() before accessing + * the requested object. + */ + +class unfetched_object : public messaging_exception +{ +public: + + unfetched_object() : messaging_exception("Object not fetched.") {} + ~unfetched_object() throw() {} +}; + + +/** The service is not currently connected. + */ + +class not_connected : public messaging_exception +{ +public: + + not_connected() : messaging_exception("Not connected to a service.") {} + ~not_connected() throw() {} +}; + + +/** The service is already connected (must disconnect before). + */ + +class already_connected : public messaging_exception +{ +public: + + already_connected() : messaging_exception("Already connected to a service. Disconnect and retry.") {} + ~already_connected() throw() {} +}; + + +/** Command error: operation failed (this is specific to the underlying protocol). + */ + +class command_error : public messaging_exception +{ +public: + + command_error(const string& command, const string& response, const string& desc = "") + : messaging_exception(desc.empty() + ? "Error while executing command '" + command + "'." + : "Error while executing command '" + command + "': " + desc + "." + ), + m_command(command), m_response(response) {} + ~command_error() throw() {} + + /** Return the name of the command which have thrown the exception. + * This is protocol-dependant. + * + * @return command name (protocol-dependant) + */ + const string& command() const { return (m_command); } + + /** Return the invalid response line. + * The meaning is protocol-dependant. + * + * @return response line (protocol-dependant) + */ + const string& response() const { return (m_response); } + +private: + + string m_command; + string m_response; +}; + + +/** The server returned an invalid response. + */ + +class invalid_response : public messaging_exception +{ +public: + + invalid_response(const string& command, const string& response) + : messaging_exception(command.empty() + ? "Received invalid response." + : "Received invalid response for command '" + command + "'." + ), + m_command(command), m_response(response) {} + ~invalid_response() throw() {} + + /** Return the name of the command which have thrown the exception. + * This is protocol-dependant. + * + * @return command name (protocol-dependant) + */ + const string& command() const { return (m_command); } + + /** Return the invalid response line. + * The meaning is protocol-dependant. + * + * @return response line (protocol-dependant) + */ + const string& response() const { return (m_response); } + +private: + + string m_command; + string m_response; +}; + + +/** Partial fetch is not supported by the underlying protocol. + */ + +class partial_fetch_not_supported : public messaging_exception +{ +public: + + partial_fetch_not_supported() : messaging_exception("Partial fetch not supported.") {} + ~partial_fetch_not_supported() throw() {} +}; + + +/** The URL is malformed. + */ + +class malformed_url : public messaging_exception +{ +public: + + malformed_url(const string& error) : messaging_exception("Malformed URL: " + error + ".") {} + ~malformed_url() throw() {} +}; + + +/** Folder name is invalid. + */ + +class invalid_folder_name : public messaging_exception +{ +public: + + invalid_folder_name(const string& error) : messaging_exception("Invalid folder name: " + error + ".") {} + ~invalid_folder_name() throw() {} +}; + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + + +#if VMIME_HAVE_FILESYSTEM_FEATURES + + +/** Base class for exceptions thrown by the filesystem features. + */ + +class filesystem_exception : public vmime::exception +{ +public: + + filesystem_exception(const string& what, const utility::path& path) : exception(what), m_path(path) {} + ~filesystem_exception() throw() {} + + /** Return the full path of the file have thrown the exception. + * + * @return full path of the file/directory + */ + const utility::path& path() const { return (m_path); } + +private: + + const utility::path m_path; +}; + + +/** File is not a directory. + */ + +class not_a_directory : public filesystem_exception +{ +public: + + not_a_directory(const utility::path& path) : filesystem_exception("Operation failed: this is not a directory.", path) {} + ~not_a_directory() throw() {} +}; + + +/** File not found. + */ + +class file_not_found : public filesystem_exception +{ +public: + + file_not_found(const utility::path& path) : filesystem_exception("File not found.", path) {} + ~file_not_found() throw() {} +}; + + +#endif // VMIME_HAVE_FILESYSTEM_FEATURES + + +} // exceptions + + +} // vmime + + +#endif // VMIME_EXCEPTION_HPP_INCLUDED diff --git a/src/fileAttachment.cpp b/src/fileAttachment.cpp new file mode 100644 index 00000000..ca7ff789 --- /dev/null +++ b/src/fileAttachment.cpp @@ -0,0 +1,123 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include +#include + +#include "fileAttachment.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +fileAttachment::fileAttachment(const string& filename, const mediaType& type, const text& desc) +{ + m_type = type; + m_desc = desc; + + setData(filename); + + m_encoding = encoding::decide(m_data); +} + + +fileAttachment::fileAttachment(const string& filename, const mediaType& type, + const class encoding& enc, const text& desc) +{ + m_type = type; + m_desc = desc; + + setData(filename); + + m_encoding = enc; +} + + +void fileAttachment::setData(const string& filename) +{ + std::ifstream* file = new std::ifstream(); + file->open(filename.c_str(), std::ios::in | std::ios::binary); + + if (!*file) + { + delete (file); + throw exceptions::open_file_error(); + } + + m_data.set(new utility::inputStreamPointerAdapter(file, true), 0, true); +} + + +void fileAttachment::generatePart(bodyPart& part) const +{ + defaultAttachment::generatePart(part); + + contentDispositionField& cdf = part.header().fields.ContentDisposition(); + + if (m_fileInfo.hasSize()) cdf.size() = toString(m_fileInfo.getSize()); + if (m_fileInfo.hasFilename()) cdf.filename() = m_fileInfo.getFilename(); + if (m_fileInfo.hasCreationDate()) cdf.creationDate() = m_fileInfo.getCreationDate(); + if (m_fileInfo.hasModificationDate()) cdf.modificationDate() = m_fileInfo.getModificationDate(); + if (m_fileInfo.hasReadDate()) cdf.readDate() = m_fileInfo.getReadDate(); +} + + +// +// fileAttachment::fileInfo +// + +fileAttachment::fileInfo::fileInfo() + : m_filename(NULL), m_size(NULL), m_creationDate(NULL), m_modifDate(NULL), m_readDate(NULL) +{ +} + + +fileAttachment::fileInfo::~fileInfo() +{ + delete (m_filename); + delete (m_size); + delete (m_creationDate); + delete (m_modifDate); + delete (m_readDate); +} + +const bool fileAttachment::fileInfo::hasFilename() const { return (m_filename != NULL); } +const string& fileAttachment::fileInfo::getFilename() const { return (*m_filename); } +void fileAttachment::fileInfo::setFilename(const string& name) { if (m_filename) { *m_filename = name; } else { m_filename = new string(name); } } + +const bool fileAttachment::fileInfo::hasCreationDate() const { return (m_creationDate != NULL); } +const datetime& fileAttachment::fileInfo::getCreationDate() const { return (*m_creationDate); } +void fileAttachment::fileInfo::setCreationDate(const datetime& date) { if (m_creationDate) { *m_creationDate = date; } else { m_creationDate = new datetime(date); } } + +const bool fileAttachment::fileInfo::hasModificationDate() const { return (m_modifDate != NULL); } +const datetime& fileAttachment::fileInfo::getModificationDate() const { return (*m_modifDate); } +void fileAttachment::fileInfo::setModificationDate(const datetime& date) { if (m_modifDate) { *m_modifDate = date; } else { m_modifDate = new datetime(date); } } + +const bool fileAttachment::fileInfo::hasReadDate() const { return (m_readDate != NULL); } +const datetime& fileAttachment::fileInfo::getReadDate() const { return (*m_readDate); } +void fileAttachment::fileInfo::setReadDate(const datetime& date) { if (m_readDate) { *m_readDate = date; } else { m_readDate = new datetime(date); } } + +const bool fileAttachment::fileInfo::hasSize() const { return (m_size != NULL); } +const unsigned int fileAttachment::fileInfo::getSize() const { return (*m_size); } +void fileAttachment::fileInfo::setSize(const unsigned int& size) { if (m_size) { *m_size = size; } else { m_size = new unsigned int(size); } } + + +} // vmime diff --git a/src/fileAttachment.hpp b/src/fileAttachment.hpp new file mode 100644 index 00000000..66cb258d --- /dev/null +++ b/src/fileAttachment.hpp @@ -0,0 +1,90 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_FILEATTACHMENT_HPP_INCLUDED +#define VMIME_FILEATTACHMENT_HPP_INCLUDED + + +#include "defaultAttachment.hpp" + + +namespace vmime +{ + + +class fileAttachment : public defaultAttachment +{ +public: + + fileAttachment(const string& filename, const mediaType& type, const text& desc = NULL_TEXT); + fileAttachment(const string& filename, const mediaType& type, const class encoding& enc, const text& desc = NULL_TEXT); + + class fileInfo + { + public: + + fileInfo(); + ~fileInfo(); + + const bool hasFilename() const; + const string& getFilename() const; + void setFilename(const string& name); + + const bool hasCreationDate() const; + const datetime& getCreationDate() const; + void setCreationDate(const datetime& date); + + const bool hasModificationDate() const; + const datetime& getModificationDate() const; + void setModificationDate(const datetime& date); + + const bool hasReadDate() const; + const datetime& getReadDate() const; + void setReadDate(const datetime& date); + + const bool hasSize() const; + const unsigned int getSize() const; + void setSize(const unsigned int& size); + + protected: + + string* m_filename; + unsigned int* m_size; + datetime* m_creationDate; + datetime* m_modifDate; + datetime* m_readDate; + }; + + const class fileInfo& fileInfo() const { return (m_fileInfo); } + class fileInfo& fileInfo() { return (m_fileInfo); } + +protected: + + void setData(const string& filename); + + class fileInfo m_fileInfo; + + void generatePart(bodyPart& part) const; +}; + + +} // vmime + + +#endif // VMIME_FILEATTACHMENT_HPP_INCLUDED diff --git a/src/header.cpp b/src/header.cpp new file mode 100644 index 00000000..f7745360 --- /dev/null +++ b/src/header.cpp @@ -0,0 +1,452 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "header.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +header::header() +{ +} + + +header::~header() +{ +} + + +/* + + RFC #822: + 3.2. HEADER FIELD DEFINITIONS + +field = field-name ":" [ field-body ] CRLF + +field-name = 1* + +field-body = field-body-contents + [CRLF LWSP-char field-body] + +field-body-contents = + +*/ + +void header::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + string::size_type pos = position; + + fields.clear(); + + while (pos < end) + { + char_t c = buffer[pos]; + + // Check for end of headers (empty line): although RFC-822 recommends + // to use CRLF for header/body separator (see 4.1 SYNTAX), here, we + // also check for LF just in case... + if (c == '\n') + { + ++pos; + break; + } + else if (c == '\r' && pos + 1 < end && buffer[pos + 1] == '\n') + { + pos += 2; + break; + } + + // This line may be a field description + if (!isspace(c)) + { + const string::size_type nameStart = pos; // remember the start position of the line + + while (pos < end && (buffer[pos] != ':' && !isspace(buffer[pos]))) + ++pos; + + const string::size_type nameEnd = pos; + + while (pos < end && isspace(buffer[pos])) + ++pos; + + if (buffer[pos] != ':') + { + // Humm...does not seem to be a valid header line. + // Skip this error and advance to the next line + pos = nameStart; + + while (pos < end && buffer[pos] != '\n') + ++pos; + + if (buffer[pos] == '\n') + ++pos; + } + else + { + // Extract the field name + const string name(buffer.begin() + nameStart, + buffer.begin() + nameEnd); + + // Skip ':' character + ++pos; + + // Skip spaces between ':' and the field contents + while (pos < end && (buffer[pos] == ' ' || buffer[pos] == '\t')) + ++pos; + + // Extract the field value + string contents; + + while (pos < end) + { + c = buffer[pos]; + + // Check for end of contents + if (c == '\r' && pos + 1 < end && buffer[pos + 1] == '\n') + { + pos += 2; + break; + } + else if (c == '\n') + { + ++pos; + break; + } + + const string::size_type ctsStart = pos; + string::size_type ctsEnd = pos; + + while (pos < end) + { + c = buffer[pos]; + + // Check for end of line + if (c == '\r' && pos + 1 < end && buffer[pos + 1] == '\n') + { + ctsEnd = pos; + pos += 2; + break; + } + else if (c == '\n') + { + ctsEnd = pos; + ++pos; + break; + } + + ++pos; + } + + if (ctsEnd != ctsStart) + { + // Append this line to contents + contents.append(buffer.begin() + ctsStart, + buffer.begin() + ctsEnd); + } + + // Handle the case of folded lines + if (buffer[pos] == ' ' || buffer[pos] == '\t') + { + // This is a folding white-space: we keep it as is and + // we continue with contents parsing... + } + else + { + // End of this field + break; + } + } + + // Add a new field to list + fields.m_fields.push_back(headerFieldFactory::getInstance()-> + create(headerField::nameToType(name), name, contents)); + } + } + else + { + // Skip this error and advance to the next line + while (pos < end && buffer[pos] != '\n') + ++pos; + + if (buffer[pos] == '\n') + ++pos; + } + } + + // If we have found the header/body separator, skip it + if (pos < end) + { + if (buffer[pos] == '\n') + { + // This is a LF (illegal but...) + ++pos; + } + else if (buffer[pos] == '\r' && pos + 1 < end) + { + // This is a CRLF + pos += 2; + } + } + + if (newPosition) + *newPosition = pos; +} + + +void header::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type /* curLinePos */, string::size_type* newLinePos) const +{ + // Generate the fields + for (std::vector ::const_iterator + it = fields.m_fields.begin() ; it != fields.m_fields.end() ; ++it) + { + (*it)->generate(os, maxLineLength); + os << CRLF; + } + + if (newLinePos) + *newLinePos = 0; +} + + +header& header::operator=(const header& h) +{ + fields = h.fields; + + return (*this); +} + + + +////////////////////// +// Fields container // +////////////////////// + + +header::fieldsContainer::fieldsContainer() +{ +} + + +header::fieldsContainer::~fieldsContainer() +{ + for (std::vector ::iterator i = m_fields.begin() ; i != m_fields.end() ; ++i) + delete (*i); +} + + +// Checks whether (at least) one field with this type/name exists +const bool header::fieldsContainer::has(const headerField::Types fieldType) const +{ + std::vector ::const_iterator pos = m_fields.begin(); + const std::vector ::const_iterator end = m_fields.end(); + + for ( ; pos != end && (*pos)->type() != fieldType ; ++pos); + + return (pos != end); +} + + +const bool header::fieldsContainer::has(const string& fieldName) const +{ + headerField::Types type = headerField::nameToType(fieldName); + if (type != headerField::Custom) return (has(type)); + + const string name = toLower(fieldName); + + std::vector ::const_iterator pos = m_fields.begin(); + const std::vector ::const_iterator end = m_fields.end(); + + for ( ; pos != end && toLower((*pos)->name()) != name ; ++pos); + + return (pos != end); +} + + +// Find the first field that matches the specified type/name. +// If no field is found, an exception is thrown. +headerField& header::fieldsContainer::find(const headerField::Types fieldType) const +{ + // Find the first field that matches the specified type + std::vector ::const_iterator pos = m_fields.begin(); + const std::vector ::const_iterator end = m_fields.end(); + + for ( ; pos != end && (*pos)->type() != fieldType ; ++pos); + + // No field with this type can be found + if (pos == end) + { + throw exceptions::no_such_field(); + } + // Else, return a reference to the existing field + else + { + return (**pos); + } +} + + +headerField& header::fieldsContainer::find(const string& fieldName) const +{ + headerField::Types type = headerField::nameToType(fieldName); + if (type != headerField::Custom) return (find(type)); + + const string name = toLower(fieldName); + + // Find the first field that matches the specified name + std::vector ::const_iterator pos = m_fields.begin(); + const std::vector ::const_iterator end = m_fields.end(); + + for ( ; pos != end && toLower((*pos)->name()) != name ; ++pos); + + // No field with this name can be found + if (pos == end) + { + throw exceptions::no_such_field(); + } + // Else, return a reference to the existing field + else + { + return (**pos); + } +} + + +// Find the first field that matches the specified type/name +headerField& header::fieldsContainer::get(const headerField::Types fieldType) +{ + // Find the first field that matches the specified type + std::vector ::const_iterator pos = m_fields.begin(); + const std::vector ::const_iterator end = m_fields.end(); + + for ( ; pos != end && (*pos)->type() != fieldType ; ++pos); + + // If no field with this type can be found, create a new one + if (pos == end) + { + headerField* field = headerFieldFactory::getInstance()->create(fieldType); + insertSorted(field); + + // Return a reference to the new field + return (*field); + } + // Else, return a reference to the existing field + else + { + return (**pos); + } +} + + +headerField& header::fieldsContainer::get(const string& fieldName) +{ + headerField::Types type = headerField::nameToType(fieldName); + if (type != headerField::Custom) return (get(type)); + + const string name = toLower(fieldName); + + // Find the first field that matches the specified name + std::vector ::const_iterator pos = m_fields.begin(); + const std::vector ::const_iterator end = m_fields.end(); + + for ( ; pos != end && toLower((*pos)->name()) != name ; ++pos); + + // If no field with this name can be found, create a new one + if (pos == end) + { + headerField* field = headerFieldFactory::getInstance()->create(fieldName); + insertSorted(field); + + // Return a reference to the new field + return (*field); + } + // Else, return a reference to the existing field + else + { + return (**pos); + } +} + + +void header::fieldsContainer::insertSorted(headerField* field) +{ + const headerField::Types type = field->type(); + std::vector ::iterator i; + + for (i = m_fields.begin() ; (i != m_fields.end()) && ((*i)->type() < type) ; ++i); + + m_fields.insert(i, field); +} + + +// Field insertion +void header::fieldsContainer::append(const headerField& field) +{ + m_fields.push_back(field.clone()); +} + + +void header::fieldsContainer::insert(const iterator it, const headerField& field) +{ + m_fields.insert(it.m_iterator, field.clone()); +} + + +// Field removing +void header::fieldsContainer::remove(const iterator it) +{ + delete (*it.m_iterator); + m_fields.erase(it.m_iterator); +} + + +void header::fieldsContainer::clear() +{ + for (std::vector ::iterator it = m_fields.begin() ; it != m_fields.end() ; ++it) + delete (*it); + + m_fields.clear(); +} + + +header::fieldsContainer& header::fieldsContainer::operator=(const fieldsContainer& c) +{ + std::vector fields; + + for (std::vector ::const_iterator it = c.m_fields.begin() ; it != c.m_fields.end() ; ++it) + fields.push_back((*it)->clone()); + + for (std::vector ::iterator it = m_fields.begin() ; it != m_fields.end() ; ++it) + delete (*it); + + m_fields.resize(fields.size()); + std::copy(fields.begin(), fields.end(), m_fields.begin()); + + return (*this); +} + + +} // vmime diff --git a/src/header.hpp b/src/header.hpp new file mode 100644 index 00000000..8ce070b9 --- /dev/null +++ b/src/header.hpp @@ -0,0 +1,266 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_HEADER_HPP_INCLUDED +#define VMIME_HEADER_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" +#include "exception.hpp" + +#include "headerField.hpp" +#include "headerFieldFactory.hpp" + +#include "addressListField.hpp" +#include "mailboxListField.hpp" +#include "mailboxField.hpp" +#include "textField.hpp" +#include "dateField.hpp" +#include "contentTypeField.hpp" +#include "contentEncodingField.hpp" +#include "defaultField.hpp" +#include "contentDispositionField.hpp" +#include "messageIdField.hpp" + + +namespace vmime +{ + + +class bodyPart; + + +/** Header section of a MIME part. + */ + +class header : public component +{ + friend class bodyPart; + friend class body; + friend class message; + +protected: + + header(); + ~header(); + +public: + + // A sub-class for field manipulation + class fieldsContainer + { + friend class header; + + protected: + + fieldsContainer(); + ~fieldsContainer(); + + public: + + // Field access + mailboxField& From() { return (dynamic_cast(get(headerField::From))); } + mailboxField& Sender() { return (dynamic_cast(get(headerField::Sender))); } + mailboxField& ReplyTo() { return (dynamic_cast(get(headerField::ReplyTo))); } + mailboxField& DeliveredTo() { return (dynamic_cast(get(headerField::DeliveredTo))); } + addressListField& To() { return (dynamic_cast(get(headerField::To))); } + addressListField& Cc() { return (dynamic_cast(get(headerField::Cc))); } + addressListField& Bcc() { return (dynamic_cast(get(headerField::Bcc))); } + dateField& Date() { return (dynamic_cast(get(headerField::Date))); } + textField& Subject() { return (dynamic_cast(get(headerField::Subject))); } + textField& Organization() { return (dynamic_cast(get(headerField::Organization))); } + textField& UserAgent() { return (dynamic_cast(get(headerField::UserAgent))); } + contentTypeField& ContentType() { return (dynamic_cast(get(headerField::ContentType))); } + textField& ContentDescription() { return (dynamic_cast(get(headerField::ContentDescription))); } + contentEncodingField& ContentTransferEncoding() { return (dynamic_cast(get(headerField::ContentTransferEncoding))); } + defaultField& MimeVersion() { return (dynamic_cast(get(headerField::MimeVersion))); } + contentDispositionField& ContentDisposition() { return (dynamic_cast(get(headerField::ContentDisposition))); } + messageIdField& ContentId() { return (dynamic_cast(get(headerField::ContentId))); } + messageIdField& MessageId() { return (dynamic_cast(get(headerField::MessageId))); } + defaultField& ContentLocation() { return (dynamic_cast(get(headerField::ContentLocation))); } + + const mailboxField& From() const { return (dynamic_cast(find(headerField::From))); } + const mailboxField& Sender() const { return (dynamic_cast(find(headerField::Sender))); } + const mailboxField& ReplyTo() const { return (dynamic_cast(find(headerField::ReplyTo))); } + const mailboxField& DeliveredTo() const { return (dynamic_cast(find(headerField::DeliveredTo))); } + const addressListField& To() const { return (dynamic_cast(find(headerField::To))); } + const addressListField& Cc() const { return (dynamic_cast(find(headerField::Cc))); } + const addressListField& Bcc() const { return (dynamic_cast(find(headerField::Bcc))); } + const dateField& Date() const { return (dynamic_cast(find(headerField::Date))); } + const textField& Subject() const { return (dynamic_cast(find(headerField::Subject))); } + const textField& Organization() const { return (dynamic_cast(find(headerField::Organization))); } + const textField& UserAgent() const { return (dynamic_cast(find(headerField::UserAgent))); } + const contentTypeField& ContentType() const { return (dynamic_cast(find(headerField::ContentType))); } + const textField& ContentDescription() const { return (dynamic_cast(find(headerField::ContentDescription))); } + const contentEncodingField& ContentTransferEncoding() const { return (dynamic_cast(find(headerField::ContentTransferEncoding))); } + const defaultField& MimeVersion() const { return (dynamic_cast(find(headerField::MimeVersion))); } + const contentDispositionField& ContentDisposition() const { return (dynamic_cast(find(headerField::ContentDisposition))); } + const messageIdField& ContentId() const { return (dynamic_cast(find(headerField::ContentId))); } + const messageIdField& MessageId() const { return (dynamic_cast(find(headerField::MessageId))); } + const defaultField& ContentLocation() const { return (dynamic_cast(find(headerField::ContentLocation))); } + + // Checks whether (at least) one field with this type/name exists + const bool has(const headerField::Types fieldType) const; + const bool has(const string& fieldName) const; + + // Find the first field that matches the specified type/name. + // If no field is found, an exception is thrown. + headerField& find(const headerField::Types fieldType) const; + headerField& find(const string& fieldName) const; + + // Find the first field that matches the specified type/name. + // If no field is found, one will be created. + headerField& get(const headerField::Types fieldType); + headerField& get(const string& fieldName); + + // Field iterator + class const_iterator; + + class iterator + { + friend class header::fieldsContainer::const_iterator; + friend class header::fieldsContainer; + + public: + + typedef std::vector ::iterator::difference_type difference_type; + + iterator(std::vector ::iterator it) : m_iterator(it) { } + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + headerField& operator*() const { return (**m_iterator); } + headerField* operator->() const { return (*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator operator++(int) { iterator i(*this); ++m_iterator; return (i); } + + iterator& operator--() { --m_iterator; return (*this); } + iterator operator--(int) { iterator i(*this); --m_iterator; return (i); } + + iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + iterator operator-(difference_type x) const { return iterator(m_iterator - x); } + + headerField& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector ::iterator m_iterator; + }; + + class const_iterator + { + public: + + typedef std::vector ::const_iterator::difference_type difference_type; + + const_iterator(std::vector ::const_iterator it) : m_iterator(it) { } + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const headerField& operator*() const { return (**m_iterator); } + const headerField* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator operator++(int) { const_iterator i(*this); ++m_iterator; return (i); } + + const_iterator& operator--() { --m_iterator; return (*this); } + const_iterator operator--(int) { const_iterator i(*this); --m_iterator; return (i); } + + const_iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + const_iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + const_iterator operator-(difference_type x) const { return const_iterator(m_iterator - x); } + + const headerField& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector ::const_iterator m_iterator; + }; + + public: + + iterator begin() { return (m_fields.begin()); } + iterator end() { return (m_fields.end()); } + + const_iterator begin() const { return (const_iterator(m_fields.begin())); } + const_iterator end() const { return (const_iterator(m_fields.end())); } + + // Field insertion + void append(const headerField& field); + void insert(const iterator it, const headerField& field); + + // Field removing + void remove(const iterator it); + void clear(); + + // Field count + const std::vector ::size_type count() const { return (m_fields.size()); } + const std::vector ::size_type size() const { return (m_fields.size()); } + const bool empty() const { return (m_fields.empty()); } + + headerField& front() { return (*m_fields.front()); } + const headerField& front() const { return (*m_fields.front()); } + headerField& back() { return (*m_fields.back()); } + const headerField& back() const { return (*m_fields.back()); } + + fieldsContainer& operator=(const fieldsContainer& c); + + protected: + + void insertSorted(headerField* field); + + std::vector m_fields; + + } fields; + + typedef fieldsContainer::iterator iterator; + typedef fieldsContainer::const_iterator const_iterator; + + header& operator=(const header& h); + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_HEADER_HPP_INCLUDED diff --git a/src/headerField.cpp b/src/headerField.cpp new file mode 100644 index 00000000..20f90251 --- /dev/null +++ b/src/headerField.cpp @@ -0,0 +1,266 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "headerField.hpp" +#include "headerFieldFactory.hpp" + + +namespace vmime +{ + + +headerField::headerField() + : m_type(Custom), m_name("Undefined") +{ +} + + +headerField::headerField(const string& fieldName) + : m_type(Custom), m_name(fieldName) +{ +} + + +headerField::~headerField() +{ +} + + +headerField* headerField::clone() const +{ + headerField* field = NULL; + + if (m_type == Custom) + field = headerFieldFactory::getInstance()->create(m_name); + else + field = headerFieldFactory::getInstance()->create(m_type); + + field->copyFrom(*this); + + return (field); +} + + +const bool headerField::operator<(const headerField& field) const +{ + return (m_type < field.m_type); +} + + +headerField& headerField::operator=(const headerField& field) +{ + copyFrom(field); + return (*this); +} + + +void headerField::copyFrom(const headerField& field) +{ + m_type = field.m_type; + m_name = field.m_name; +} + + +void headerField::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + if (m_type == Custom) + { + os << m_name + ": "; + + if (newLinePos) + *newLinePos = curLinePos + m_name.length() + 2; + } + else + { + const string name = typeToName(m_type); + + os << name + ": "; + + if (newLinePos) + *newLinePos = curLinePos + name.length() + 2; + } +} + + +/** Return the field type corresponding to the specified name. + * + * @param name field name + * @return field type (see headerField::Types) or headerField::custom + * if this is a custom field + */ + +const headerField::Types headerField::nameToType(const string& name) +{ + switch (name[0]) + { + case 'B': + case 'b': + { + if (isStringEqualNoCase(name, "bcc", 3)) return (Bcc); + break; + } + case 'C': + case 'c': + { + if (isStringEqualNoCase(name, "cc", 2)) return (Cc); + else if (isStringEqualNoCase(name, "content-type", 12)) return (ContentType); + else if (isStringEqualNoCase(name, "content-transfer-encoding", 25)) return (ContentTransferEncoding); + else if (isStringEqualNoCase(name, "content-description", 19)) return (ContentDescription); + else if (isStringEqualNoCase(name, "content-disposition", 19)) return (ContentDisposition); + else if (isStringEqualNoCase(name, "content-id", 10)) return (ContentId); + else if (isStringEqualNoCase(name, "content-location", 16)) return (ContentLocation); + break; + } + case 'd': + case 'D': + { + if (isStringEqualNoCase(name, "date", 4)) return (Date); + else if (isStringEqualNoCase(name, "delivered-to", 12)) return (DeliveredTo); + break; + } + case 'f': + case 'F': + { + if (isStringEqualNoCase(name, "from", 4)) return (From); + break; + } + case 'm': + case 'M': + { + if (isStringEqualNoCase(name, "mime-version", 12)) return (MimeVersion); + else if (isStringEqualNoCase(name, "message-id", 10)) return (MessageId); + break; + } + case 'o': + case 'O': + { + if (isStringEqualNoCase(name, "organization", 12)) return (Organization); + break; + } + case 'r': + case 'R': + { + if (isStringEqualNoCase(name, "received", 8)) return (Received); + else if (isStringEqualNoCase(name, "reply-to", 8)) return (ReplyTo); + else if (isStringEqualNoCase(name, "return-path", 11)) return (ReturnPath); + break; + } + case 's': + case 'S': + { + if (isStringEqualNoCase(name, "sender", 6)) return (Sender); + else if (isStringEqualNoCase(name, "subject", 7)) return (Subject); + break; + } + case 't': + case 'T': + { + if (isStringEqualNoCase(name, "to", 2)) return (To); + break; + } + case 'u': + case 'U': + { + if (isStringEqualNoCase(name, "user-agent", 10)) return (UserAgent); + break; + } + + } + + return (Custom); +} + + +/** Return the name for the specified field type. + * Eg: returns "From" for headerField::From. + * + * @param type field type + * @return name for the specified field type + */ + +const string headerField::typeToName(const Types type) +{ + switch (type) + { + case From: return "From"; + case Sender: return "Sender"; + case To: return "To"; + case Cc: return "Cc"; + case Bcc: return "Bcc"; + case Date: return "Date"; + case Received: return "Received"; + case Subject: return "Subject"; + case ReplyTo: return "Reply-To"; + case Organization: return "Organization"; + case DeliveredTo: return "Delivered-To"; + case UserAgent: return "User-Agent"; + case ReturnPath: return "Return-Path"; + case ContentType: return "Content-Type"; + case ContentTransferEncoding: return "Content-Transfer-Encoding"; + case ContentDescription: return "Content-Description"; + case MimeVersion: return "Mime-Version"; + case ContentDisposition: return "Content-Disposition"; + case ContentId: return "Content-Id"; + case MessageId: return "Message-Id"; + case ContentLocation: return "Content-Location"; + + case Custom: + case Last: + return "?"; + }; + + return "?"; +} + + +/** Return the type of this field. + * + * @return field type (see headerField::Types) + */ + +const headerField::Types headerField::type() const +{ + return (m_type); +} + + +/** Return the name of this field. + * + * @return field name + */ + +const string headerField::name() const +{ + return ((m_type == Custom) ? m_name : typeToName(m_type)); +} + + +/** Check whether this field is a custom field. + * + * @return true if the field is a custom field, false otherwise + */ + +const bool headerField::isCustom() const +{ + return (m_type == Custom); +} + + +} // vmime diff --git a/src/headerField.hpp b/src/headerField.hpp new file mode 100644 index 00000000..70a74eff --- /dev/null +++ b/src/headerField.hpp @@ -0,0 +1,113 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_HEADERFIELD_HPP_INCLUDED +#define VMIME_HEADERFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + + +namespace vmime +{ + + +/** Base class for header fields. + */ + +class headerField : public component +{ + friend class headerFieldFactory; + +protected: + + headerField(); + headerField(const string& fieldName); + +public: + + ~headerField(); + +public: + + // Header field types (in the order in which they will appear + // in the message header) + enum Types + { + Received, // Relay + From, // Expeditor + Sender, // Sender + ReplyTo, // Reply-To + To, // Recipient(s) + Cc, // Carbon copy recipient(s) + Bcc, // Blind carbon-copy recipient(s) + Date, // Date sent + Subject, // Subject + Organization, // Organization + UserAgent, // User agent + DeliveredTo, // Delivered-To + ReturnPath, // Return-Path + MimeVersion, // Mime-Version + MessageId, // Message-Id + ContentType, // Content-Type + ContentTransferEncoding, // Content-Transfer-Encoding + ContentDescription, // Content-Description + ContentDisposition, // Content-Disposition + ContentId, // Content-Id + ContentLocation, // Content-Location + + Custom, // Unknown or custom field (eg. X-Priority, X-Mailer, etc.) + + Last + }; + +protected: + + Types m_type; + string m_name; // In case of custom field + +public: + + const bool operator<(const headerField& field) const; + + const Types type() const; + const string name() const; + + const bool isCustom() const; + + virtual void copyFrom(const headerField& field); + headerField& operator=(const headerField& field); + headerField* clone() const; + + static const Types nameToType(const string& name); + static const string typeToName(const Types type); + + + // Component assembling + using component::generate; + + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_HEADERFIELD_HPP_INCLUDED diff --git a/src/headerFieldFactory.cpp b/src/headerFieldFactory.cpp new file mode 100644 index 00000000..0187c26d --- /dev/null +++ b/src/headerFieldFactory.cpp @@ -0,0 +1,139 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "headerFieldFactory.hpp" +#include "exception.hpp" + +#include "defaultField.hpp" + +#include "mailboxField.hpp" +#include "addressListField.hpp" +#include "addressListField.hpp" +#include "addressListField.hpp" +#include "mailboxField.hpp" +#include "dateField.hpp" +#include "relayField.hpp" +#include "textField.hpp" +#include "mailboxField.hpp" +#include "contentTypeField.hpp" +#include "contentEncodingField.hpp" +#include "contentDispositionField.hpp" +#include "messageIdField.hpp" + + +namespace vmime +{ + + +headerFieldFactory::headerFieldFactory() +{ + // Register some default field types + registerType (headerField::From); + registerType (headerField::To); + registerType (headerField::Cc); + registerType (headerField::Bcc); + registerType (headerField::Sender); + registerType (headerField::Date); + registerType (headerField::Received); + registerType (headerField::Subject); + registerType (headerField::ReplyTo); + registerType (headerField::DeliveredTo); + registerType (headerField::Organization); + registerType (headerField::UserAgent); + registerType (headerField::ReturnPath); + registerType (headerField::ContentType); + registerType (headerField::ContentTransferEncoding); + registerType (headerField::ContentDescription); + registerType (headerField::MimeVersion); + registerType (headerField::ContentDisposition); + registerType (headerField::ContentId); + registerType (headerField::MessageId); + registerType (headerField::ContentLocation); +} + + +headerFieldFactory::~headerFieldFactory() +{ +} + + +headerField* headerFieldFactory::create + (const string& name, const string& body) +{ + const headerField::Types type = headerField::nameToType(name); + + if (type != headerField::Custom) + { + return (create(type, name, body)); + } + else + { + NameMap::const_iterator pos = m_nameMap.find(toLower(name)); + headerField* field = NULL; + + if (pos != m_nameMap.end()) + { + field = ((*pos).second)(); + } + else + { + field = new defaultField; + } + + field->m_type = headerField::Custom; + field->m_name = name; + + if (body != NULL_STRING) + field->parse(body); + + return (field); + } +} + + +headerField* headerFieldFactory::create(const headerField::Types type, + const string& name, const string& body) +{ + if (type == headerField::Custom) + { + return (create(name, body)); + } + else + { + TypeMap::const_iterator pos = m_typeMap.find(type); + + if (pos != m_typeMap.end()) + { + headerField* field = ((*pos).second)(); + + field->m_type = type; + if (name != NULL_STRING) field->m_name = name; + if (body != NULL_STRING) field->parse(body); + + return (field); + } + else + { + throw exceptions::bad_field_type(); + } + } +} + + +} // vmime diff --git a/src/headerFieldFactory.hpp b/src/headerFieldFactory.hpp new file mode 100644 index 00000000..13024fd6 --- /dev/null +++ b/src/headerFieldFactory.hpp @@ -0,0 +1,85 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_HEADERFIELDFACTORY_HPP_INCLUDED +#define VMIME_HEADERFIELDFACTORY_HPP_INCLUDED + + +#include "headerField.hpp" +#include "utility/singleton.hpp" + + +namespace vmime +{ + + +class headerFieldFactory : public utility::singleton +{ + friend class utility::singleton ; + +protected: + + headerFieldFactory(); + ~headerFieldFactory(); + + typedef headerField* (*AllocFunc)(void); + typedef std::map NameMap; + typedef std::map TypeMap; + + NameMap m_nameMap; + TypeMap m_typeMap; + +public: + + template + class registerer + { + public: + + static headerField* creator() + { + // Allocate a new object + return new TYPE(); + } + }; + + + template + void registerName(const string& name) + { + m_nameMap.insert(NameMap::value_type(toLower(name), ®isterer::creator)); + } + + headerField* create(const string& name, const string& body = NULL_STRING); + headerField* create(const headerField::Types type, const string& name = NULL_STRING, const string& body = NULL_STRING); + +protected: + + template + void registerType(const headerField::Types type) + { + m_typeMap.insert(TypeMap::value_type(type, ®isterer::creator)); + } +}; + + +} // vmime + + +#endif // VMIME_HEADERFIELDFACTORY_HPP_INCLUDED diff --git a/src/htmlTextPart.cpp b/src/htmlTextPart.cpp new file mode 100644 index 00000000..f864272d --- /dev/null +++ b/src/htmlTextPart.cpp @@ -0,0 +1,371 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "htmlTextPart.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +htmlTextPart::~htmlTextPart() +{ +} + + +const mediaType htmlTextPart::type() const +{ + return (mediaType(mediaTypes::TEXT, mediaTypes::TEXT_HTML)); +} + + +const int htmlTextPart::getPartCount() const +{ + return (m_plainText.empty() ? 1 : 2); +} + + +void htmlTextPart::generateIn(bodyPart& /* message */, bodyPart& parent) const +{ + // Plain text + if (!m_plainText.empty()) + { + // -- Create a new part + bodyPart* part = new bodyPart(); + parent.body().parts.append(part); + + // -- Set header fields + part->header().fields.ContentType() = mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN); + part->header().fields.ContentType().charset() = m_charset; + part->header().fields.ContentTransferEncoding() = encoding(encodingTypes::QUOTED_PRINTABLE); + + // -- Set contents + part->body().contents() = m_plainText; + } + + // HTML text + // -- Create a new part + bodyPart* htmlPart = new bodyPart(); + + // -- Set header fields + htmlPart->header().fields.ContentType() = mediaType(mediaTypes::TEXT, mediaTypes::TEXT_HTML); + htmlPart->header().fields.ContentType().charset() = m_charset; + htmlPart->header().fields.ContentTransferEncoding() = encoding(encodingTypes::QUOTED_PRINTABLE); + + // -- Set contents + htmlPart->body().contents() = m_text; + + // Handle the case we have embedded objects + if (!embeddedObjects.empty()) + { + // Create a "multipart/related" body part + bodyPart* relPart = new bodyPart(); + parent.body().parts.append(relPart); + + relPart->header().fields.ContentType() = mediaType + (mediaTypes::MULTIPART, mediaTypes::MULTIPART_RELATED); + + // Add the HTML part into this part + relPart->body().parts.append(htmlPart); + + // Also add images into this part + for (embeddedObjectsContainer::const_iterator i = embeddedObjects.begin() ; + i != embeddedObjects.end() ; ++i) + { + bodyPart* objPart = new bodyPart(); + relPart->body().parts.append(objPart); + + string id = (*i).id(); + + if (id.substr(0, 4) == "CID:") + id = id.substr(4); + + objPart->header().fields.ContentType() = (*i).type(); + objPart->header().fields.ContentId() = messageId("<" + id + ">"); + objPart->header().fields.ContentDisposition() = disposition(dispositionTypes::INLINE); + objPart->header().fields.ContentTransferEncoding() = (*i).encoding(); + //encoding(encodingTypes::BASE64); + + objPart->body().contents() = (*i).data(); + } + } + else + { + // Add the HTML part into the parent part + parent.body().parts.append(htmlPart); + } +} + + +void htmlTextPart::findEmbeddedParts(const bodyPart& part, + std::vector & cidParts, std::vector & locParts) +{ + for (body::const_iterator p = part.body().parts.begin() ; p != part.body().parts.end() ; ++p) + { + try + { + dynamic_cast((*p).header().fields.find(headerField::ContentId)); + cidParts.push_back(&(*p)); + } + catch (exceptions::no_such_field) + { + // No "Content-id" field. + // Maybe there is a "Content-Location" field... + try + { + dynamic_cast((*p).header().fields.find(headerField::ContentId)); + locParts.push_back(&(*p)); + } + catch (exceptions::no_such_field) + { + // No "Content-Location" field. + // Cannot be an embedded object since it cannot be referenced in HTML text. + } + } + + findEmbeddedParts((*p), cidParts, locParts); + } +} + + +void htmlTextPart::addEmbeddedObject(const bodyPart& part, const string& id) +{ + mediaType type; + + try + { + const contentTypeField& ctf = dynamic_cast + (part.header().fields.find(headerField::ContentType)); + + type = ctf.value(); + } + catch (exceptions::no_such_field) + { + // No "Content-type" field: assume "application/octet-stream". + } + + embeddedObjects.m_list.push_back(new embeddedObject + (part.body().contents(), part.body().encoding(), id, type)); +} + + +void htmlTextPart::parse(const bodyPart& message, const bodyPart& parent, const bodyPart& textPart) +{ + // Search for possible embedded objects in the _whole_ message. + std::vector cidParts; + std::vector locParts; + + findEmbeddedParts(message, cidParts, locParts); + + // Extract HTML text + std::ostringstream oss; + utility::outputStreamAdapter adapter(oss); + + textPart.body().contents().extract(adapter); + + const string data = oss.str(); + + m_text = textPart.body().contents(); + + try + { + const contentTypeField& ctf = dynamic_cast + (textPart.header().fields.find(headerField::ContentType)); + + m_charset = ctf.charset(); + } + catch (exceptions::no_such_field) + { + // No "Content-type" field. + } + catch (exceptions::no_such_parameter) + { + // No "charset" parameter. + } + + // Extract embedded objects. The algorithm is quite simple: for each previously + // found inline part, we check if its CID/Location is contained in the HTML text. + for (std::vector ::const_iterator p = cidParts.begin() ; p != cidParts.end() ; ++p) + { + const messageIdField& midField = dynamic_cast + ((**p).header().fields.find(headerField::ContentId)); + + const string searchFor("CID:" + midField.value().id()); + + if (data.find(searchFor) != string::npos) + { + // This part is referenced in the HTML text. + // Add it to the embedded object list. + addEmbeddedObject(**p, "CID:" + midField.value().id()); + } + } + + for (std::vector ::const_iterator p = locParts.begin() ; p != locParts.end() ; ++p) + { + const defaultField& locField = dynamic_cast + ((**p).header().fields.find(headerField::ContentLocation)); + + if (data.find(locField.value()) != string::npos) + { + // This part is referenced in the HTML text. + // Add it to the embedded object list. + addEmbeddedObject(**p, locField.value()); + } + } + + // Extract plain text, if any. + findPlainTextPart(message, parent, textPart); +} + + +bool htmlTextPart::findPlainTextPart(const bodyPart& part, const bodyPart& parent, const bodyPart& textPart) +{ + // We search for the nearest "multipart/alternative" part. + try + { + const contentTypeField& ctf = dynamic_cast + (part.header().fields.find(headerField::ContentType)); + + if (ctf.value().type() == mediaTypes::MULTIPART && + ctf.value().subType() == mediaTypes::MULTIPART_ALTERNATIVE) + { + bodyPart const* foundPart = NULL; + + for (body::const_iterator p = part.body().parts.begin() ; !foundPart && p != part.body().parts.end() ; ++p) + { + if (&(*p) == &parent || // if "text/html" is in "multipart/related" + &(*p) == &textPart) // if not... + { + foundPart = &(*p); + } + } + + if (foundPart) + { + bool found = false; + + // Now, search for the alternative plain text part + for (body::const_iterator p = part.body().parts.begin() ; + !found && p != part.body().parts.end() ; ++p) + { + try + { + const contentTypeField& ctf = dynamic_cast + ((*p).header().fields.find(headerField::ContentType)); + + if (ctf.value().type() == mediaTypes::TEXT && + ctf.value().subType() == mediaTypes::TEXT_PLAIN) + { + m_plainText = (*p).body().contents(); + found = true; + } + } + catch (exceptions::no_such_field) + { + // No "Content-type" field. + } + } + + // If we don't have found the plain text part here, it means that + // it does not exists (the MUA which built this message probably + // did not include it...). + return (found); + } + } + } + catch (exceptions::no_such_field) + { + // No "Content-type" field. + } + + bool found = false; + + for (body::const_iterator p = part.body().parts.begin() ; !found && p != part.body().parts.end() ; ++p) + { + found = findPlainTextPart(*p, parent, textPart); + } + + return (found); +} + + + +//////////////////////////////// +// Embedded objects container // +//////////////////////////////// + + +htmlTextPart::embeddedObjectsContainer::~embeddedObjectsContainer() +{ + free_container(m_list); +} + + +const htmlTextPart::embeddedObject& htmlTextPart::embeddedObjectsContainer::find(const string& id) const +{ + for (std::vector ::const_iterator o = m_list.begin() ; o != m_list.end() ; ++o) + { + if ((**o).id() == id) + return (**o); + } + + throw exceptions::no_object_found(); +} + + +const bool htmlTextPart::embeddedObjectsContainer::has(const string& id) const +{ + for (std::vector ::const_iterator o = m_list.begin() ; o != m_list.end() ; ++o) + { + if ((**o).id() == id) + return (true); + } + + return (false); +} + + +const string htmlTextPart::embeddedObjectsContainer::add + (const contentHandler& data, const vmime::encoding& enc, const mediaType& type) +{ + const messageId mid(messageId::generateId()); + const string id = "CID:" + mid.id(); + + m_list.push_back(new embeddedObject(data, enc, id, type)); + + return (id); +} + + +const string htmlTextPart::embeddedObjectsContainer::add + (const contentHandler& data, const mediaType& type) +{ + return (add(data, encoding::decide(data), type)); +} + + +const string htmlTextPart::embeddedObjectsContainer::add + (const string& data, const mediaType& type) +{ + return (add(contentHandler(data), encoding::decide(data), type)); +} + + +} // vmime diff --git a/src/htmlTextPart.hpp b/src/htmlTextPart.hpp new file mode 100644 index 00000000..eeb7b618 --- /dev/null +++ b/src/htmlTextPart.hpp @@ -0,0 +1,180 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_HTMLTEXTPART_HPP_INCLUDED +#define VMIME_HTMLTEXTPART_HPP_INCLUDED + + +#include "textPart.hpp" +#include "messageId.hpp" +#include "encoding.hpp" + +#include "contentHandler.hpp" + + +namespace vmime +{ + + +class htmlTextPart : public textPart +{ +protected: + + ~htmlTextPart(); + +public: + + const mediaType type() const; + + const vmime::charset& charset() const { return (m_charset); } + vmime::charset& charset() { return (m_charset); } + + const contentHandler& plainText() const { return (m_plainText); } + contentHandler& plainText() { return (m_plainText); } + + const contentHandler& text() const { return (m_text); } + contentHandler& text() { return (m_text); } + + // Embedded object (eg. image for tag) + class embeddedObject + { + public: + + embeddedObject(const contentHandler& data, const vmime::encoding& enc, + const string& id, const mediaType& type) + : m_data(data), m_encoding(enc), m_id(id), m_type(type) + { + } + + public: + + const contentHandler& data() const { return (m_data); } + const vmime::encoding& encoding() const { return (m_encoding); } + const string& id() const { return (m_id); } + const mediaType& type() const { return (m_type); } + + private: + + contentHandler m_data; + vmime::encoding m_encoding; + string m_id; + mediaType m_type; + }; + + // Embedded objects container + class embeddedObjectsContainer + { + friend class htmlTextPart; + + protected: + + ~embeddedObjectsContainer(); + + public: + + // Test the existence/get an embedded object given its identifier. + const bool has(const string& id) const; + const embeddedObject& find(const string& id) const; + + // Embed an object and returns a string which identifies it. + const string add(const string& data, const mediaType& type); + const string add(const contentHandler& data, const mediaType& type); + const string add(const contentHandler& data, const encoding& enc, const mediaType& type); + + // Embedded objects enumerator + class const_iterator + { + public: + + typedef std::vector ::const_iterator::difference_type difference_type; + + const_iterator(std::vector ::const_iterator it) : m_iterator(it) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const embeddedObject& operator*() const { return (**m_iterator); } + const embeddedObject* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator operator++(int) { const_iterator i(*this); ++m_iterator; return (i); } + + const_iterator& operator--() { --m_iterator; return (*this); } + const_iterator operator--(int) { const_iterator i(*this); --m_iterator; return (i); } + + const_iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + const_iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + const_iterator operator-(difference_type x) const { return const_iterator(m_iterator - x); } + + const embeddedObject& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector ::const_iterator m_iterator; + }; + + public: + + const_iterator begin() const { return (const_iterator(m_list.begin())); } + const_iterator end() const { return (const_iterator(m_list.end())); } + + // Object count + const std::vector ::size_type count() const { return (m_list.size()); } + const std::vector ::size_type size() const { return (m_list.size()); } + const bool empty() const { return (m_list.empty()); } + + embeddedObject& front() { return (*m_list.front()); } + const embeddedObject& front() const { return (*m_list.front()); } + embeddedObject& back() { return (*m_list.back()); } + const embeddedObject& back() const { return (*m_list.back()); } + + protected: + + std::vector m_list; + + } embeddedObjects; + + typedef embeddedObjectsContainer::const_iterator const_iterator; + +protected: + + contentHandler m_plainText; + contentHandler m_text; + vmime::charset m_charset; + + void findEmbeddedParts(const bodyPart& part, std::vector & cidParts, std::vector & locParts); + void addEmbeddedObject(const bodyPart& part, const string& id); + + bool findPlainTextPart(const bodyPart& part, const bodyPart& parent, const bodyPart& textPart); + + const int getPartCount() const; + + void generateIn(bodyPart& message, bodyPart& parent) const; + virtual void parse(const bodyPart& message, const bodyPart& parent, const bodyPart& textPart); +}; + + +} // vmime + + +#endif // VMIME_HTMLTEXTPART_HPP_INCLUDED diff --git a/src/mailbox.cpp b/src/mailbox.cpp new file mode 100644 index 00000000..6560296c --- /dev/null +++ b/src/mailbox.cpp @@ -0,0 +1,450 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "mailbox.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +mailbox::mailbox() +{ +} + + +mailbox::mailbox(const class mailbox& mailbox) + : address(), m_name(mailbox.m_name), m_email(mailbox.m_email) +{ +} + + +mailbox::mailbox(const string& email) + : m_email(email) +{ +} + + +mailbox::mailbox(const text& name, const string& email) + : m_name(name), m_email(email) +{ +} + + +/* + + RFC #2822: + 3.4. ADDRESS SPECIFICATION + +mailbox = name-addr / addr-spec + +name-addr = [display-name] angle-addr + +angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr + +*/ + +void mailbox::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* const pstart = buffer.data() + position; + const string::value_type* p = pstart; + + // Ignore blank spaces at the beginning + while (p < pend && isspace(*p)) ++p; + + // Current state for parsing machine + enum States + { + State_None, + State_Name, + State_Address + }; + + States state = State_Name; // let's start with name, we will see later (*) + + // Temporary buffers for extracted name and address + string name; + string address; + + while (p < pend) + { + if (state == State_Name) + { + if (*p == '<') + { + state = State_Address; + continue; + } + + if (*p == '"') // Quoted string + { + ++p; + + bool escaped = false; + + while (p < pend) + { + if (escaped) + { + name += *p; + escaped = false; + } + else if (*p == '\\') + { + escaped = true; + } + else + { + if (*p == '"') + { + ++p; + break; + } + else + { + name += *p; + } + } + + ++p; + } + } + else + { + bool escaped = false; + int comment = 0; + + while (p < pend) + { + if (escaped) + { + if (!comment) name += *p; + escaped = false; + } + else if (comment) + { + if (*p == '\\') + escaped = true; + else if (*p == '(') + ++comment; + else if (*p == ')') + --comment; + } + else if (*p == '\\') + { + escaped = true; + } + else if (*p == '(') + { + ++comment; + } + else if (*p == '<') + { + // Erase any space between display name and
+ string::iterator q = name.end(); + for ( ; q != name.begin() && isspace(*(q - 1)) ; --q); + name.erase(q, name.end()); + + break; + } + else if (/* isspace(*p) || */ *p == '@') + { + break; + } + else + { + name += *p; + } + + ++p; + } + } + + if (p < pend && *p == '@') + { + // (*) Actually, we were parsing the local-part of an address + // and not a display name... + address = name; + name.clear(); + + bool escaped = false; + int comment = 0; + + while (p < pend) + { + if (escaped) + { + if (!comment) address += *p; + escaped = false; + } + else if (comment) + { + if (*p == '\\') + escaped = true; + else if (*p == '(') + ++comment; + else if (*p == ')') + --comment; + } + else if (*p == '\\') + { + escaped = true; + } + else if (*p == '(') + { + ++comment; + } + else if (isspace(*p)) + { + break; + } + else + { + address += *p; + } + + ++p; + } + + break; + } + else + { + while (p < pend && isspace(*p)) ++p; + state = State_None; + } + } + else if (state == State_Address) + { + // Skip '<' character + if (*p == '<') + ++p; + + bool escaped = false; + int comment = 0; + + while (p < pend) + { + if (escaped) + { + if (!comment) address += *p; + escaped = false; + } + else if (comment) + { + if (*p == '\\') + escaped = true; + else if (*p == '(') + ++comment; + else if (*p == ')') + --comment; + } + else if (*p == '(') + { + ++comment; + } + else if (*p == '\\') + { + escaped = true; + } + else if (*p == '<') + { + // If we found a '<' here, it means that the address + // starts _only_ here...and the stuff we have parsed + // before belongs actually to the display name! + name += address; + address.clear(); + } + else if (*p == '>') + { + break; + } + else if (!isspace(*p)) + { + address += *p; + } + + ++p; + } + + break; + } + else + { + while (p < pend && isspace(*p)) ++p; + + if (p < pend) + { + //if (*p == '<') + state = State_Address; + } + } + } + + decodeAndUnfoldText(name, m_name); + m_email = address; + + if (newPosition) + *newPosition = position + (p - pstart); +} + + +void mailbox::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + if (m_name.empty()) + { + bool newLine = false; + + // No display name is specified, only email address. + if (curLinePos /* + 2 */ + m_email.length() > maxLineLength) + { + os << NEW_LINE_SEQUENCE; + newLine = true; + } + + //os << "<" << m_email << ">"; + os << m_email; + + if (newLinePos) + { + *newLinePos = curLinePos + m_email.length() /* + 2 */; + if (newLine) *newLinePos += 1; + } + } + else + { + // We have to encode the name: + // - if it contains characters in a charset different from "US-ASCII", + // - and/or if it contains one or more of these special chars: + // SPACE TAB " ; , < > ( ) @ / ? . = : + + // Check whether there are words that are not "US-ASCII" + // and/or contain the special chars. + bool forceEncode = false; + + for (text::const_iterator w = m_name.begin() ; !forceEncode && w != m_name.end() ; ++w) + { + if ((*w).charset() == charset(charsets::US_ASCII)) + { + const string& buffer = (*w).buffer(); + + for (string::const_iterator c = buffer.begin() ; + !forceEncode && c != buffer.end() ; ++c) + { + switch (*c) + { + case ' ': + case '\t': + case ';': + case ',': + case '<': case '>': + case '(': case ')': + case '@': + case '/': + case '?': + case '.': + case '=': + case ':': + case '"': + + forceEncode = true; + break; + } + } + } + else + { + forceEncode = true; + } + } + + string::size_type pos = curLinePos; + bool newLine = true; + + encodeAndFoldText(os, m_name, maxLineLength, pos, &pos, + forceEncode ? encodeAndFoldFlags::forceEncoding : encodeAndFoldFlags::none); + + if (pos + m_email.length() + 3 > maxLineLength) + { + os << NEW_LINE_SEQUENCE; + newLine = true; + } + + os << " <" << m_email << ">"; + + if (newLinePos) + { + *newLinePos = pos + m_email.length() + 3; + if (newLine) *newLinePos += NEW_LINE_SEQUENCE.length(); + } + } +} + + +const bool mailbox::operator==(const class mailbox& mailbox) const +{ + return (m_name == mailbox.m_name && m_email == mailbox.m_email); +} + + +const bool mailbox::operator!=(const class mailbox& mailbox) const +{ + return !(*this == mailbox); +} + + +void mailbox::copyFrom(const address& addr) +{ + const mailbox& source = dynamic_cast(addr); + + m_name = source.m_name; + m_email = source.m_email; +} + + +address* mailbox::clone() const +{ + return new mailbox(*this); +} + + +const bool mailbox::empty() const +{ + return (m_email.empty()); +} + + +void mailbox::clear() +{ + m_name.clear(); + m_email.clear(); +} + + +const bool mailbox::isGroup() const +{ + return (false); +} + + +} // vmime diff --git a/src/mailbox.hpp b/src/mailbox.hpp new file mode 100644 index 00000000..3fa79045 --- /dev/null +++ b/src/mailbox.hpp @@ -0,0 +1,105 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MAILBOX_HPP_INCLUDED +#define VMIME_MAILBOX_HPP_INCLUDED + + +#include "address.hpp" +#include "text.hpp" + + +namespace vmime +{ + + +/** A mailbox: full name + email (basic type). + */ + +class mailbox : public address +{ + friend class mailboxGroup; + friend class mailboxField; + +public: + + mailbox(); + mailbox(const class mailbox& mailbox); + mailbox(const string& email); + mailbox(const text& name, const string& email); + + /** Return the full name of the mailbox (empty if not specified). + * + * @return full name of the mailbox + */ + const text& name() const { return (m_name); } + + /** Return the full name of the mailbox (empty if not specified). + * + * @return full name of the mailbox + */ + text& name() { return (m_name); } + + /** Return the email of the mailbox. + * + * @return email of the mailbox + */ + const string& email() const { return (m_email); } + + /** Return the email of the mailbox. + * + * @return email of the mailbox + */ + string& email() { return (m_email); } + + // Comparison + const bool operator==(const class mailbox& mailbox) const; + const bool operator!=(const class mailbox& mailbox) const; + + // Assignment + void copyFrom(const address& addr); + address* clone() const; + + const bool empty() const; + + void clear(); + + + const bool isGroup() const; + +protected: + + text m_name; + string m_email; + +public: + + using address::parse; + using address::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MAILBOX_HPP_INCLUDED diff --git a/src/mailboxField.cpp b/src/mailboxField.cpp new file mode 100644 index 00000000..e4e0164c --- /dev/null +++ b/src/mailboxField.cpp @@ -0,0 +1,95 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "mailboxField.hpp" +#include "mailboxGroup.hpp" + + +namespace vmime +{ + + +mailboxField::mailboxField() +{ +} + + +void mailboxField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_mailbox.clear(); + + // Here, we cannot simply call "m_mailbox.parse()" because it + // may have more than one address specified (even if this field + // should contain only one). We are never too much careful... + address* parsedAddress = address::parseNext(buffer, position, end, newPosition); + + if (parsedAddress) + { + if (parsedAddress->isGroup()) + { + // If it is a group of mailboxes, take the first + // mailbox of the group + mailboxGroup* group = static_cast (parsedAddress); + + if (!group->empty()) + m_mailbox = *(group->begin()); + } + else + { + // Parse only if it is a mailbox + m_mailbox = *static_cast (parsedAddress); + } + } + + delete (parsedAddress); + + if (newPosition) + *newPosition = end; +} + + +void mailboxField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + m_mailbox.generate(os, maxLineLength, pos, newLinePos); +} + + +mailboxField& mailboxField::operator=(const class mailbox& mailbox) +{ + m_mailbox = mailbox; + return (*this); +} + + +void mailboxField::copyFrom(const headerField& field) +{ + const mailboxField& source = dynamic_cast(field); + m_mailbox = source.m_mailbox; + + headerField::copyFrom(field); +} + + +} // vmime diff --git a/src/mailboxField.hpp b/src/mailboxField.hpp new file mode 100644 index 00000000..13cd8e49 --- /dev/null +++ b/src/mailboxField.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MAILBOXFIELD_HPP_INCLUDED +#define VMIME_MAILBOXFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "mailbox.hpp" + + +namespace vmime +{ + + +class mailboxField : public headerField +{ + friend class headerFieldFactory::registerer ; + +protected: + + mailboxField(); + +public: + + void copyFrom(const headerField& field); + + mailboxField& operator=(const class mailbox& mailbox); + + const mailbox& value() const { return (m_mailbox); } + mailbox& value() { return (m_mailbox); } + +protected: + + mailbox m_mailbox; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MAILBOXFIELD_HPP_INCLUDED diff --git a/src/mailboxGroup.cpp b/src/mailboxGroup.cpp new file mode 100644 index 00000000..ff708313 --- /dev/null +++ b/src/mailboxGroup.cpp @@ -0,0 +1,237 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "mailboxGroup.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +mailboxGroup::mailboxGroup() +{ +} + + +mailboxGroup::mailboxGroup(const class mailboxGroup& mailboxGroup) + : address() +{ + copyFrom(mailboxGroup); +} + + +mailboxGroup::mailboxGroup(const text& name) + : m_name(name) +{ +} + + +mailboxGroup::~mailboxGroup() +{ + clear(); +} + + +void mailboxGroup::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* const pstart = buffer.data() + position; + const string::value_type* p = pstart; + + while (p < pend && isspace(*p)) + ++p; + + string name; + + while (p < pend && *p != ':') + { + name += *p; + ++p; + } + + if (p < pend && *p == ':') + ++p; + + + string::size_type pos = position + (p - pstart); + + while (pos < end) + { + address* parsedAddress = address::parseNext(buffer, pos, end, &pos); + + if (parsedAddress) + { + if (parsedAddress->isGroup()) + { + mailboxGroup* group = static_cast (parsedAddress); + + // Sub-groups are not allowed in mailbox groups: so, we add all + // the contents of the sub-group into this group... + for (mailboxGroup::const_iterator + it = group->begin() ; it != group->end() ; ++it) + { + m_list.push_back(static_cast ((*it).clone())); + } + + delete (parsedAddress); + } + else + { + m_list.push_back(static_cast (parsedAddress)); + } + } + } + + decodeAndUnfoldText(name, m_name); + + if (newPosition) + *newPosition = end; +} + + +void mailboxGroup::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + // We have to encode the name: + // - if it contains characters in a charset different from "US-ASCII", + // - and/or if it contains one or more of these special chars: + // SPACE TAB " ; , < > ( ) @ / ? . = : + + // Check whether there are words that are not "US-ASCII" + // and/or contain the special chars. + bool forceEncode = false; + + for (text::const_iterator w = m_name.begin() ; !forceEncode && w != m_name.end() ; ++w) + { + if ((*w).charset() == charset(charsets::US_ASCII)) + { + const string& buffer = (*w).buffer(); + + for (string::const_iterator c = buffer.begin() ; + !forceEncode && c != buffer.end() ; ++c) + { + switch (*c) + { + case ' ': + case '\t': + case ';': + case ',': + case '<': case '>': + case '(': case ')': + case '@': + case '/': + case '?': + case '.': + case '=': + case ':': + + forceEncode = true; + break; + } + } + } + } + + string::size_type pos = curLinePos; + + encodeAndFoldText(os, m_name, maxLineLength - 2, pos, &pos, + forceEncode ? encodeAndFoldFlags::forceEncoding : encodeAndFoldFlags::none); + + os << ":"; + ++pos; + + for (const_iterator it = m_list.begin() ; it != m_list.end() ; ++it) + { + if (it != m_list.begin()) + { + os << ", "; + pos += 2; + } + else + { + os << " "; + ++pos; + } + + (*it).generate(os, maxLineLength - 2, pos, &pos); + } + + os << ";"; + pos++; + + if (newLinePos) + *newLinePos = pos; +} + + +address* mailboxGroup::clone() const +{ + return new mailboxGroup(*this); +} + + +// Mailbox insertion +void mailboxGroup::append(const mailbox& field) +{ + m_list.push_back(static_cast(field.clone())); +} + + +void mailboxGroup::insert(const iterator it, const mailbox& field) +{ + m_list.insert(it.m_iterator, static_cast(field.clone())); +} + + +// Mailbox removing +void mailboxGroup::erase(const iterator it) +{ + delete (*it.m_iterator); + m_list.erase(it.m_iterator); +} + + +void mailboxGroup::clear() +{ + free_container(m_list); +} + + +void mailboxGroup::copyFrom(const address& addr) +{ + const mailboxGroup& source = dynamic_cast(addr); + + m_name = source.m_name; + + clear(); + + for (std::vector ::const_iterator i = source.m_list.begin() ; i != source.m_list.end() ; ++i) + m_list.push_back(static_cast((*i)->clone())); +} + + +const bool mailboxGroup::isGroup() const +{ + return (true); +} + + +} // vmime diff --git a/src/mailboxGroup.hpp b/src/mailboxGroup.hpp new file mode 100644 index 00000000..0187f37c --- /dev/null +++ b/src/mailboxGroup.hpp @@ -0,0 +1,155 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MAILBOXGROUP_HPP_INCLUDED +#define VMIME_MAILBOXGROUP_HPP_INCLUDED + + +#include "address.hpp" +#include "mailbox.hpp" +#include "text.hpp" + + +namespace vmime +{ + + +/** A group of mailboxes (basic type). + */ + +class mailboxGroup : public address +{ +public: + + mailboxGroup(); + mailboxGroup(const class mailboxGroup& mailboxGroup); + mailboxGroup(const text& name); + + ~mailboxGroup(); + + // Properties set/get + const text& name() const { return (m_name); } + text& name() { return (m_name); } + + // Assignment + void copyFrom(const address& addr); + address* clone() const; + +public: + + // Mailbox iterator + class const_iterator; + + class iterator + { + friend class mailboxGroup; + friend class const_iterator; + + public: + + iterator(std::vector ::iterator it) : m_iterator(it) { } + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + mailbox& operator*() const { return (**m_iterator); } + mailbox* operator->() const { return (*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator& operator++(int) { ++m_iterator; return (*this); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + private: + + std::vector ::iterator m_iterator; + }; + + class const_iterator + { + friend class mailboxGroup; + + public: + + const_iterator(std::vector ::const_iterator it) : m_iterator(it) { } + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const mailbox& operator*() const { return (**m_iterator); } + const mailbox* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator& operator++(int) { ++m_iterator; return (*this); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + private: + + std::vector ::const_iterator m_iterator; + }; + + iterator begin() { return (m_list.begin()); } + iterator end() { return (m_list.end()); } + + const_iterator begin() const { return (const_iterator(m_list.begin())); } + const_iterator end() const { return (const_iterator(m_list.end())); } + + const std::vector ::size_type size() const { return (m_list.size()); } + const std::vector ::size_type count() const { return (m_list.size()); } + const bool empty() const { return (m_list.empty()); } + + const mailbox& operator[](const std::vector ::size_type x) const { return (*m_list[x]); } + mailbox& operator[](const std::vector ::size_type x) { return (*m_list[x]); } + + // Mailbox insertion + virtual void append(const mailbox& field); + virtual void insert(const iterator it, const mailbox& field); + + // Mailbox removing + void erase(const iterator it); + void clear(); + + + const bool isGroup() const; + +protected: + + text m_name; + std::vector m_list; + +public: + + using address::parse; + using address::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MAILBOXGROUP_HPP_INCLUDED diff --git a/src/mailboxList.cpp b/src/mailboxList.cpp new file mode 100644 index 00000000..ded7ede1 --- /dev/null +++ b/src/mailboxList.cpp @@ -0,0 +1,46 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "mailboxList.hpp" + + +namespace vmime +{ + + +// Address insertion +void mailboxList::append(const address& addr) +{ + // Ensure this is a "mailbox" object + const mailbox& mb = dynamic_cast(addr); + + m_list.push_back(mb.clone()); +} + + +void mailboxList::insert(const iterator it, const address& addr) +{ + // Ensure this is a "mailbox" object + const mailbox& mb = dynamic_cast(addr); + + m_list.insert(it.m_iterator, mb.clone()); +} + + +} // vmime diff --git a/src/mailboxList.hpp b/src/mailboxList.hpp new file mode 100644 index 00000000..28c6152b --- /dev/null +++ b/src/mailboxList.hpp @@ -0,0 +1,127 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MAILBOXLIST_HPP_INCLUDED +#define VMIME_MAILBOXLIST_HPP_INCLUDED + + +#include "addressList.hpp" +#include "mailbox.hpp" + + +namespace vmime +{ + + +/** A list of mailboxes (basic type). + */ + +class mailboxList : public addressList +{ + friend class mailboxGroup; + +public: + + // + // The following functions have the same name and work *exactly* like + // the ones in "addressList", except we don't accept anything other + // than objects of type "mailbox" (instead of a generic "address"). + // + // This prevents user from inserting mailbox groups where it is not + // allowed by the RFC. + // + + // Address iterator + class const_iterator; + + class iterator + { + friend class mailboxList; + friend class const_iterator; + + protected: + + iterator(std::vector ::iterator it) : m_iterator(it) { } + + public: + + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + mailbox& operator*() const { return static_cast(**m_iterator); } + mailbox* operator->() const { return static_cast(*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator& operator++(int) { ++m_iterator; return (*this); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + private: + + std::vector ::iterator m_iterator; + }; + + class const_iterator + { + friend class mailboxList; + + protected: + + const_iterator(std::vector ::const_iterator it) : m_iterator(it) { } + + public: + + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const mailbox& operator*() const { return static_cast(**m_iterator); } + const mailbox* operator->() const { return static_cast(*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator& operator++(int) { ++m_iterator; return (*this); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + private: + + std::vector ::const_iterator m_iterator; + }; + + iterator begin() { return (m_list.begin()); } + iterator end() { return (m_list.end()); } + + const_iterator begin() const { return (const_iterator(m_list.begin())); } + const_iterator end() const { return (const_iterator(m_list.end())); } + + // Address insertion + void append(const address& addr); + void insert(const iterator it, const address& addr); +}; + + +} // vmime + + +#endif // VMIME_MAILBOXLIST_HPP_INCLUDED diff --git a/src/mailboxListField.cpp b/src/mailboxListField.cpp new file mode 100644 index 00000000..69c328f5 --- /dev/null +++ b/src/mailboxListField.cpp @@ -0,0 +1,60 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "mailboxListField.hpp" + + +namespace vmime +{ + + +mailboxListField::mailboxListField() +{ +} + + +void mailboxListField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_list.parse(buffer, position, end, newPosition); +} + + +void mailboxListField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + m_list.generate(os, maxLineLength, pos, newLinePos); +} + + +void mailboxListField::copyFrom(const headerField& field) +{ + const mailboxListField& source = dynamic_cast(field); + m_list = source.m_list; + + headerField::copyFrom(field); +} + + +} // vmime + diff --git a/src/mailboxListField.hpp b/src/mailboxListField.hpp new file mode 100644 index 00000000..6e01c278 --- /dev/null +++ b/src/mailboxListField.hpp @@ -0,0 +1,68 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MAILBOXLISTFIELD_HPP_INCLUDED +#define VMIME_MAILBOXLISTFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "mailboxList.hpp" + + +namespace vmime +{ + + +class mailboxListField : public headerField +{ + friend class headerFieldFactory::registerer ; + +protected: + + mailboxListField(); + +public: + + void copyFrom(const headerField& field); + + const mailboxList& value() const { return (m_list); } + mailboxList& value() { return (m_list); } + +protected: + + mailboxList m_list; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MAILBOXLISTFIELD_HPP_INCLUDED diff --git a/src/mediaType.cpp b/src/mediaType.cpp new file mode 100644 index 00000000..6681a0dc --- /dev/null +++ b/src/mediaType.cpp @@ -0,0 +1,127 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "mediaType.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +mediaType::mediaType() + : m_type(mediaTypes::APPLICATION), m_subType(mediaTypes::APPLICATION_OCTET_STREAM) +{ +} + + +mediaType::mediaType(const string& type) +{ + parse(type); +} + + +mediaType::mediaType(const string& type, const string& subType) +{ + set(type, subType); +} + + +void mediaType::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* const pstart = buffer.data() + position; + const string::value_type* p = pstart; + + // Extract the type + const string::size_type typeStart = position; + + while (p < pend && *p != '/') ++p; + + m_type = toLower(string(buffer.begin() + typeStart, + buffer.begin() + position + (p - pstart))); + + if (p < pend) + { + // Skip '/' character + ++p; + + // Extract the sub-type + m_subType = toLower(string(buffer.begin() + position + (p - pstart), + buffer.begin() + end)); + } + + if (newPosition) + *newPosition = end; +} + + +void mediaType::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + const string value = toLower(m_type) + "/" + toLower(m_subType); + + if (curLinePos + value.length() > maxLineLength) + { + os << NEW_LINE_SEQUENCE; + os << value; + + if (newLinePos) + *newLinePos = NEW_LINE_SEQUENCE_LENGTH + value.length(); + } + else + { + os << value; + + if (newLinePos) + *newLinePos = curLinePos + value.length(); + } +} + + +const bool mediaType::operator==(const mediaType& type) const +{ + return (m_type == type.m_type && m_subType == type.m_subType); +} + + +const bool mediaType::operator!=(const mediaType& type) const +{ + return !(*this == type); +} + + +mediaType& mediaType::operator=(const mediaType& type) +{ + m_type = type.m_type; + m_subType = type.m_subType; + + return (*this); +} + + +mediaType& mediaType::operator=(const string& type) +{ + parse(type); + return (*this); +} + + +} // vmime diff --git a/src/mediaType.hpp b/src/mediaType.hpp new file mode 100644 index 00000000..f4148e5c --- /dev/null +++ b/src/mediaType.hpp @@ -0,0 +1,78 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MEDIATYPE_HPP_INCLUDED +#define VMIME_MEDIATYPE_HPP_INCLUDED + + +#include "component.hpp" + + +namespace vmime +{ + + +/** Content media type (basic type). + */ + +class mediaType : public component +{ +public: + + mediaType(); + mediaType(const string& type); + mediaType(const string& type, const string& subType); + +public: + + const bool operator==(const mediaType& type) const; + const bool operator!=(const mediaType& type) const; + + mediaType& operator=(const mediaType& type); + mediaType& operator=(const string& type); + + const string& type() const { return (m_type); }; + string& type() { return (m_type); } + + const string& subType() const { return (m_subType); }; + string& subType() { return (m_subType); } + + void set(const string& type) { parse(type); } + void set(const string& type, const string& subType) { m_type = type; m_subType = subType; } + +protected: + + string m_type; + string m_subType; + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MEDIATYPE_HPP_INCLUDED diff --git a/src/message.cpp b/src/message.cpp new file mode 100644 index 00000000..bd6f6085 --- /dev/null +++ b/src/message.cpp @@ -0,0 +1,65 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "message.hpp" +#include "options.hpp" + +#include + + +namespace vmime +{ + + +message::message() +{ +} + + +void message::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + // We override this function to change the default value for the + // "maxLineLength" parameter. So, the user will just have to call + // message::generate() without any argument to use the maximum line + // length specified in vmime::options... + bodyPart::generate(os, maxLineLength, curLinePos, newLinePos); +} + + +const string message::generate(const string::size_type maxLineLength, + const string::size_type curLinePos) const +{ + std::ostringstream oss; + utility::outputStreamAdapter adapter(oss); + + generate(adapter, maxLineLength, curLinePos, NULL); + + return (oss.str()); +} + + +void message::parse(const string& buffer) +{ + bodyPart::parse(buffer); +} + + +} // vmime + diff --git a/src/message.hpp b/src/message.hpp new file mode 100644 index 00000000..9b3c6cc9 --- /dev/null +++ b/src/message.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGE_HPP_INCLUDED +#define VMIME_MESSAGE_HPP_INCLUDED + + +#include "bodyPart.hpp" +#include "options.hpp" + + +namespace vmime +{ + + +/** A MIME message. + */ + +class message : public bodyPart +{ +public: + + message(); + + + // Component parsing & assembling + void generate(utility::outputStream& os, const string::size_type maxLineLength = options::getInstance()->message.maxLineLength(), const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + + const string generate(const string::size_type maxLineLength = options::getInstance()->message.maxLineLength(), const string::size_type curLinePos = 0) const; + + void parse(const string& buffer); +}; + + + +} // vmime + + +#endif // VMIME_MESSAGE_HPP_INCLUDED diff --git a/src/messageBuilder.cpp b/src/messageBuilder.cpp new file mode 100644 index 00000000..52ee6390 --- /dev/null +++ b/src/messageBuilder.cpp @@ -0,0 +1,188 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "messageBuilder.hpp" + +#include "textPartFactory.hpp" + + +namespace vmime +{ + + +messageBuilder::messageBuilder() + : m_textPart(NULL) +{ + // By default there is one text part of type "text/plain" + constructTextPart(mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN)); +} + + +messageBuilder::~messageBuilder() +{ + delete (m_textPart); + + free_container(m_attach); +} + + +message* messageBuilder::construct() const +{ + // Create a new message + message* msg = new message; + + // Generate the header fields + msg->header().fields.Subject() = m_subject; + + if (m_from.empty()) + throw exceptions::no_expeditor(); + + if (m_to.empty() || (*m_to.begin()).empty()) + throw exceptions::no_recipient(); + + msg->header().fields.From() = m_from; + msg->header().fields.To() = m_to; + + if (!m_cc.empty()) + msg->header().fields.Cc() = m_cc; + + if (!m_bcc.empty()) + msg->header().fields.Bcc() = m_bcc; + + // Add a "Date" field + msg->header().fields.Date() = datetime::now(); + + // Add a "Mime-Version" header field + msg->header().fields.MimeVersion().value() = MIME_VERSION; + + // If there is one or more attachments (or other parts that are + // not "text/...") and if there is more than one parts for the + // text part, we generate these text parts into a sub-part: + // + // [message] + // | + // +-- multipart/mixed + // | + // +-- multipart/alternative + // | | + // | +-- text part #1 (eg. plain text "text/plain") + // | +-- text part #2 (eg. HTML "text/html") + // | +-- ... + // | + // +-- application/octet-stream (attachment #1) + // | + // +-- ... (other attachments/parts) + // + if (!m_attach.empty() && m_textPart->getPartCount() > 1) + { + // Set parent part (message) to "multipart/mixed" + msg->header().fields.ContentType() = mediaType + (mediaTypes::MULTIPART, mediaTypes::MULTIPART_MIXED); + + // Create a sub-part "multipart/alternative" for text parts + bodyPart* subPart = new bodyPart; + msg->body().parts.append(subPart); + + subPart->header().fields.ContentType() = mediaType + (mediaTypes::MULTIPART, mediaTypes::MULTIPART_ALTERNATIVE); + + // Generate the text parts into this sub-part (normally, this + // sub-part will have the "multipart/alternative" content-type...) + m_textPart->generateIn(*msg, *subPart); + } + else + { + // Generate the text part(s) directly into the message + m_textPart->generateIn(*msg, *msg); + + // If any attachment, set message content-type to "multipart/mixed" + if (!m_attach.empty()) + { + msg->header().fields.ContentType() = mediaType + (mediaTypes::MULTIPART, mediaTypes::MULTIPART_MIXED); + } + // Else, set it to "multipart/alternative" if there are more than one text part. + else if (m_textPart->getPartCount() > 1) + { + msg->header().fields.ContentType() = mediaType + (mediaTypes::MULTIPART, mediaTypes::MULTIPART_ALTERNATIVE); + } + } + + // Generate the attachments + if (!m_attach.empty()) + { + for (std::vector ::const_iterator a = m_attach.begin() ; a != m_attach.end() ; ++a) + { + (*a)->generateIn(*msg); + } + } + + // If there is only one part in the message, move it into the message + // (hence, the message will not be multipart...) + if (msg->body().parts.size() == 1) + { + const bodyPart& part = msg->body().parts.front(); + + // First, copy (and replace) the header fields + const header::fieldsContainer& hdr = part.header().fields; + + for (header::const_iterator f = hdr.begin() ; f != hdr.end() ; ++f) + msg->header().fields.get((*f).name()) = *f; + + // Second, copy the body contents and sub-parts (this also remove + // the body part we are copying...) + msg->body() = part.body(); + } + + return (msg); +} + + +void messageBuilder::attach(attachment* attach) +{ + m_attach.push_back(attach); +} + + +void messageBuilder::constructTextPart(const mediaType& type) +{ + class textPart* part = NULL; + + try + { + part = textPartFactory::getInstance()->create(type); + } + catch (exceptions::no_factory_available& e) + { + throw; + } + + delete (m_textPart); + m_textPart = part; +} + + +class textPart& messageBuilder::textPart() +{ + return (*m_textPart); +} + + +} // vmime diff --git a/src/messageBuilder.hpp b/src/messageBuilder.hpp new file mode 100644 index 00000000..ce7d34b1 --- /dev/null +++ b/src/messageBuilder.hpp @@ -0,0 +1,99 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGEBUILDER_HPP_INCLUDED +#define VMIME_MESSAGEBUILDER_HPP_INCLUDED + + +#include "base.hpp" + +#include "mailbox.hpp" +#include "addressList.hpp" +#include "text.hpp" +#include "message.hpp" +#include "mediaType.hpp" +#include "attachment.hpp" +#include "textPart.hpp" +#include "bodyPart.hpp" + + +namespace vmime +{ + + +/** A helper for building MIME messages. + */ + +class messageBuilder +{ +public: + + messageBuilder(); + ~messageBuilder(); + +public: + + // Expeditor and recipients + const mailbox& expeditor() const { return (m_from); } + mailbox& expeditor() { return (m_from); } + + const addressList& recipients() const { return (m_to); } + addressList& recipients() { return (m_to); } + + const addressList& copyRecipients() const { return (m_cc); } + addressList& copyRecipients() { return (m_cc); } + + const addressList& blindCopyRecipients() const { return (m_bcc); } + addressList& blindCopyRecipients() { return (m_bcc); } + + // Subject + const text& subject() const { return (m_subject); } + text& subject() { return (m_subject); } + + // Attachements + void attach(attachment* attach); + const std::vector & attachments() const { return (m_attach); } + + // Text parts + void constructTextPart(const mediaType& type); + class textPart& textPart(); + + // Construction + message* construct() const; + +protected: + + mailbox m_from; + + addressList m_to; + addressList m_cc; + addressList m_bcc; + + text m_subject; + + class textPart* m_textPart; + + std::vector m_attach; +}; + + +} // vmime + + +#endif // VMIME_MESSAGEBUILDER_HPP_INCLUDED diff --git a/src/messageId.cpp b/src/messageId.cpp new file mode 100644 index 00000000..1ea99d1f --- /dev/null +++ b/src/messageId.cpp @@ -0,0 +1,184 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "messageId.hpp" +#include "utility/random.hpp" +#include "platformDependant.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +messageId::messageId() +{ +} + + +messageId::messageId(const string& id) +{ + parse(id); +} + + +messageId::messageId(const messageId& mid) + : component(), m_left(mid.m_left), m_right(mid.m_right) +{ +} + + +messageId::messageId(const string& left, const string& right) + : m_left(left), m_right(right) +{ +} + + +/* + RFC-2822: + 3.6.4. Identification fields + + msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] +*/ + +void messageId::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* const pstart = buffer.data() + position; + const string::value_type* p = pstart; + + m_left.clear(); + m_right.clear(); + + unsigned int commentLevel = 0; + bool escape = false; + bool stop = false; + + for ( ; !stop && p < pend ; ++p) + { + if (escape) + { + // Ignore this character + } + else + { + switch (*p) + { + case '(': ++commentLevel; break; + case ')': --commentLevel; break; + case '\\': escape = true; break; + case '<': + { + if (commentLevel == 0) + { + stop = true; + break; + } + } + + } + } + } + + if (p < pend) + { + // Extract left part + const string::size_type leftStart = position + (p - pstart); + + while (p < pend && *p != '@') ++p; + + m_left = string(buffer.begin() + leftStart, + buffer.begin() + position + (p - pstart)); + + if (p < pend) + { + // Skip '@' + ++p; + + // Extract right part + const string::size_type rightStart = position + (p - pstart); + + while (p < pend && *p != '>') ++p; + + m_right = string(buffer.begin() + rightStart, + buffer.begin() + position + (p - pstart)); + } + } + + if (newPosition) + *newPosition = end; +} + + +const string messageId::id() const +{ + return (m_left + '@' + m_right); +} + + +void messageId::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + os << '<' << m_left << '@' << m_right << '>'; + + if (newLinePos) + *newLinePos = curLinePos + m_left.length() + m_right.length() + 3; +} + + +messageId& messageId::operator=(const messageId& source) +{ + m_left = source.m_left; + m_right = source.m_right; + return (*this); +} + + +messageId& messageId::operator=(const string& id) +{ + parse(id); + return (*this); +} + + +messageId messageId::generateId() +{ + std::ostringstream left; + + left << "vmime"; + left << '.'; + left << std::hex << utility::random::time(); + left << '.'; + left << std::hex << utility::random::process(); + left << '.'; + left << std::hex << utility::random::next(); + left << std::hex << utility::random::next(); + + return (messageId(left.str(), platformDependant::getHandler()->getHostName())); +} + + +const bool messageId::operator==(const messageId& mid) const +{ + return (m_left == mid.m_left && m_right == mid.m_right); +} + + +} // vmime diff --git a/src/messageId.hpp b/src/messageId.hpp new file mode 100644 index 00000000..e280dfaf --- /dev/null +++ b/src/messageId.hpp @@ -0,0 +1,82 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGEID_HPP_INCLUDED +#define VMIME_MESSAGEID_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + + +namespace vmime +{ + + +/** Message identifier (basic type). + */ + +class messageId : public component +{ +public: + + messageId(); + messageId(const string& id); + messageId(const messageId& mid); + messageId(const string& left, const string& right); + +public: + + const string& left() const { return (m_left); } + string& left() { return (m_left); } + + const string& right() const { return (m_right); } + string& right() { return (m_right); } + +public: + + messageId& operator=(const messageId& source); + messageId& operator=(const string& id); + + const bool operator==(const messageId& mid) const; + + static messageId generateId(); + + const string id() const; + +protected: + + string m_left; + string m_right; + +public: + + using component::parse; + using component::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MESSAGEID_HPP_INCLUDED diff --git a/src/messageIdField.cpp b/src/messageIdField.cpp new file mode 100644 index 00000000..ecdb901c --- /dev/null +++ b/src/messageIdField.cpp @@ -0,0 +1,66 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "messageIdField.hpp" + + +namespace vmime +{ + + +messageIdField::messageIdField() +{ +} + + +void messageIdField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_id.parse(buffer, position, end, newPosition); +} + + +void messageIdField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + m_id.generate(os, maxLineLength, pos, newLinePos); +} + + +messageIdField& messageIdField::operator=(const messageId& mid) +{ + m_id = mid; + return (*this); +} + + +void messageIdField::copyFrom(const headerField& field) +{ + const messageIdField& source = dynamic_cast(field); + m_id = source.m_id; + + headerField::copyFrom(field); +} + + +} // vmime diff --git a/src/messageIdField.hpp b/src/messageIdField.hpp new file mode 100644 index 00000000..4809e5ed --- /dev/null +++ b/src/messageIdField.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGEIDFIELD_HPP_INCLUDED +#define VMIME_MESSAGEIDFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "messageId.hpp" + + +namespace vmime +{ + + +class messageIdField : public headerField +{ + friend class headerFieldFactory::registerer ; + +protected: + + messageIdField(); + +public: + + void copyFrom(const headerField& field); + + messageIdField& operator=(const messageId& mid); + + const messageId& value() const { return (m_id); } + messageId& value() { return (m_id); } + +protected: + + messageId m_id; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_MESSAGEIDFIELD_HPP_INCLUDED diff --git a/src/messageParser.cpp b/src/messageParser.cpp new file mode 100644 index 00000000..d68c95e9 --- /dev/null +++ b/src/messageParser.cpp @@ -0,0 +1,321 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "messageParser.hpp" + +#include "defaultAttachment.hpp" +#include "textPartFactory.hpp" + +#include "relayField.hpp" + + +namespace vmime +{ + + +messageParser::messageParser(const string& buffer) +{ + vmime::message msg; + msg.parse(buffer); + + parse(msg); +} + + +messageParser::messageParser(const message& msg) +{ + parse(msg); +} + + +messageParser::~messageParser() +{ + free_container(m_attach); + free_container(m_textParts); + + for (std::map ::iterator + it = m_attachInfo.begin() ; it != m_attachInfo.end() ; ++it) + { + delete ((*it).second); + } +} + + +void messageParser::parse(const message& msg) +{ + // Header fields (if field is present, copy its value, else do nothing) +#define TRY_FIELD(x) try { x; } catch (exceptions::no_such_field) { } + TRY_FIELD(m_from = dynamic_cast(msg.header().fields.find(headerField::From)).value()); + + TRY_FIELD(m_to = dynamic_cast(msg.header().fields.find(headerField::To)).value()); + TRY_FIELD(m_cc = dynamic_cast(msg.header().fields.find(headerField::Cc)).value()); + TRY_FIELD(m_bcc = dynamic_cast(msg.header().fields.find(headerField::Bcc)).value()); + + TRY_FIELD(m_subject = dynamic_cast(msg.header().fields.find(headerField::Subject)).value()); +#undef TRY_FIELD + + // Date + try + { + vmime::relayField& recv = static_cast(msg.header().fields.find(headerField::Received)); + m_date = recv.date(); + } + catch (vmime::exceptions::no_such_field&) + { + try + { + vmime::dateField& date = static_cast(msg.header().fields.find(headerField::Date)); + m_date = date.value(); + } + catch (vmime::exceptions::no_such_field&) + { + m_date = datetime::now(); + } + } + + // Attachments + findAttachments(msg); + + // Text parts + findTextParts(msg, msg); +} + + +void messageParser::findAttachments(const bodyPart& part) +{ + // We simply search for parts that are not "Content-disposition: inline". + for (body::const_iterator p = part.body().parts.begin() ; p != part.body().parts.end() ; ++p) + { + const header& hdr = (*p).header(); + const body& bdy = (*p).body(); + + // Is this part an attachment? + bool isAttachment = false; + const contentDispositionField* contentDispField = NULL; + + try + { + const contentDispositionField& cdf = dynamic_cast + (hdr.fields.find(headerField::ContentDisposition)); + + if (cdf.value().name() != dispositionTypes::INLINE) + { + contentDispField = &cdf; + isAttachment = true; + } + } + catch (exceptions::no_such_field) + { + // No "Content-disposition" field: assume "attachment" if + // type is not "text/..." or "multipart/...". + mediaType type; + + try + { + const contentTypeField& ctf = dynamic_cast + (hdr.fields.find(headerField::ContentType)); + + type = ctf.value(); + } + catch (exceptions::no_such_field) + { + // No "Content-type" field: assume "application/octet-stream". + type = mediaType(mediaTypes::APPLICATION, + mediaTypes::APPLICATION_OCTET_STREAM); + } + + if (type.type() != mediaTypes::TEXT && type.type() != mediaTypes::MULTIPART) + isAttachment = true; + } + + if (isAttachment) + { + // Determine the media type of this attachment + mediaType type; + + try + { + const contentTypeField& ctf = dynamic_cast + (hdr.fields.find(headerField::ContentType)); + + type = ctf.value(); + } + catch (exceptions::no_such_field) + { + // No "Content-type" field: assume "application/octet-stream". + type = mediaType(mediaTypes::APPLICATION, + mediaTypes::APPLICATION_OCTET_STREAM); + } + + // Get the description (if available) + text description; + + try + { + const textField& cd = dynamic_cast + (hdr.fields.find(headerField::ContentDescription)); + + description = cd.value(); + } + catch (exceptions::no_such_field) + { + // No description available. + } + + // Construct the attachment object + attachment* attach = new defaultAttachment + (bdy.contents(), bdy.encoding(), type, description); + + if (contentDispField != NULL) + { + m_attachInfo.insert(std::map :: + value_type(attach, static_cast + (contentDispField->clone()))); + } + + // Add the attachment to the list + m_attach.push_back(attach); + } + + // Try to find attachments in sub-parts + if (bdy.parts.size()) + findAttachments(*p); + } +} + + +void messageParser::findTextParts(const bodyPart& msg, const bodyPart& part) +{ + // Handle the case in which the message is not multipart: if the body part is + // "text/*", take this part. + if (part.body().parts.count() == 0) + { + mediaType type(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN); + bool accept = false; + + try + { + const contentTypeField& ctf = dynamic_cast + (msg.header().fields.find(headerField::ContentType)); + + if (ctf.value().type() == mediaTypes::TEXT) + { + type = ctf.value(); + accept = true; + } + } + catch (exceptions::no_such_field) + { + // No "Content-type" field: assume "text/plain". + accept = true; + } + + if (accept) + { + textPart* textPart = textPartFactory::getInstance()->create(type); + textPart->parse(msg, msg, msg); + + m_textParts.push_back(textPart); + } + } + // Multipart message + else + { + findSubTextParts(msg, part); + } +} + + +bool messageParser::findSubTextParts(const bodyPart& msg, const bodyPart& part) +{ + // In general, all the text parts are contained in parallel in the same + // parent part (or message). + // So, wherever the text parts are, all we have to do is to find the first + // MIME part which is a text part. + + std::vector textParts; + + for (body::const_iterator p = part.body().parts.begin() ; + p != part.body().parts.end() ; ++p) + { + try + { + const contentTypeField& ctf = dynamic_cast + ((*p).header().fields.find(headerField::ContentType)); + + if (ctf.value().type() == mediaTypes::TEXT) + { + textParts.push_back(&(*p)); + } + } + catch (exceptions::no_such_field) + { + // No "Content-type" field. + } + } + + if (textParts.size()) + { + // Okay. So we have found at least one text part + for (std::vector ::const_iterator p = textParts.begin() ; p != textParts.end() ; ++p) + { + const contentTypeField& ctf = dynamic_cast + ((*p)->header().fields.find(headerField::ContentType)); + + try + { + textPart* textPart = textPartFactory::getInstance()->create(ctf.value()); + textPart->parse(msg, part, **p); + + m_textParts.push_back(textPart); + } + catch (exceptions::no_factory_available& e) + { + // Content-type not recognized. + } + } + + //return true; + } + + //else + { + bool found = false; + + for (body::const_iterator p = part.body().parts.begin() ; + !found && p != part.body().parts.end() ; ++p) + { + found = findSubTextParts(msg, *p); + } + + return found; + } +} + + +const contentDispositionField* messageParser::attachmentInfo(attachment* a) const +{ + std::map ::const_iterator + it = m_attachInfo.find(a); + + return (it != m_attachInfo.end() ? (*it).second : NULL); +} + + +} // vmime diff --git a/src/messageParser.hpp b/src/messageParser.hpp new file mode 100644 index 00000000..d3dc1478 --- /dev/null +++ b/src/messageParser.hpp @@ -0,0 +1,98 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGEPARSER_HPP_INCLUDED +#define VMIME_MESSAGEPARSER_HPP_INCLUDED + + +#include "base.hpp" + +#include "message.hpp" +#include "attachment.hpp" + +#include "textPart.hpp" + + +namespace vmime +{ + + +/** A helper for parsing MIME messages. + */ + +class messageParser +{ +public: + + messageParser(const string& buffer); + messageParser(const message& msg); + ~messageParser(); + +public: + + // Expeditor and recipients + const mailbox& expeditor() const { return (m_from); } + + const addressList& recipients() const { return (m_to); } + const addressList& copyRecipients() const { return (m_cc); } + const addressList& blindCopyRecipients() const { return (m_bcc); } + + // Subject + const text& subject() const { return (m_subject); } + + // Date + const datetime& date() const { return (m_date); } + + // Attachments + const std::vector & attachments() const { return (m_attach); } + const contentDispositionField* attachmentInfo(attachment* a) const; + + // Text parts + const std::vector & textParts() const { return (m_textParts); } + +protected: + + mailbox m_from; + + addressList m_to; + addressList m_cc; + addressList m_bcc; + + text m_subject; + + datetime m_date; + + std::vector m_attach; + std::map m_attachInfo; + + std::vector m_textParts; + + void parse(const message& msg); + + void findAttachments(const bodyPart& part); + + void findTextParts(const bodyPart& msg, const bodyPart& part); + bool findSubTextParts(const bodyPart& msg, const bodyPart& part); +}; + + +} // vmime + + +#endif // VMIME_MESSAGEPARSER_HPP_INCLUDED diff --git a/src/messaging/IMAPConnection.cpp b/src/messaging/IMAPConnection.cpp new file mode 100644 index 00000000..007cf353 --- /dev/null +++ b/src/messaging/IMAPConnection.cpp @@ -0,0 +1,263 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "IMAPTag.hpp" +#include "IMAPConnection.hpp" +#include "IMAPUtils.hpp" +#include "IMAPStore.hpp" + +#include "../exception.hpp" +#include "../platformDependant.hpp" + +#include + + +namespace vmime { +namespace messaging { + + +IMAPConnection::IMAPConnection(IMAPStore* store, authenticator* auth) + : m_store(store), m_auth(auth), m_socket(NULL), m_parser(NULL), m_tag(NULL), + m_hierarchySeparator('\0'), m_state(STATE_NONE), m_timeoutHandler(NULL) +{ +} + + +IMAPConnection::~IMAPConnection() +{ + if (isConnected()) + disconnect(); + else if (m_socket) + internalDisconnect(); + + delete (m_tag); + delete (m_parser); +} + + +void IMAPConnection::connect() +{ + if (isConnected()) + throw exceptions::already_connected(); + + m_state = STATE_NONE; + m_hierarchySeparator = '\0'; + + const string address = m_store->session().properties()[m_store->infos().propertyPrefix() + "server.address"]; + const port_t port = m_store->session().properties().get(m_store->infos().propertyPrefix() + "server.port", m_store->infos().defaultPort()); + + // Create the time-out handler + if (session().properties().exists + (m_store->infos().propertyPrefix() + "timeout.factory")) + { + timeoutHandlerFactory* tof = platformDependant::getHandler()-> + getTimeoutHandlerFactory(session().properties() + [m_store->infos().propertyPrefix() + "timeout.factory"]); + + m_timeoutHandler = tof->create(); + } + + // Create and connect the socket + socketFactory* sf = platformDependant::getHandler()->getSocketFactory + (m_store->session().properties().get + (m_store->infos().propertyPrefix() + "server.socket-factory", string("default"))); + + m_socket = sf->create(); + m_socket->connect(address, port); + + delete (m_tag); + m_tag = new IMAPTag(); + + delete (m_parser); + m_parser = new IMAPParser(m_tag, m_socket, m_timeoutHandler); + + + setState(STATE_NON_AUTHENTICATED); + + + // Connection greeting + // + // eg: C: + // --- S: * OK mydomain.org IMAP4rev1 v12.256 server ready + + utility::auto_ptr greet(m_parser->readGreeting()); + + if (greet->resp_cond_bye()) + { + internalDisconnect(); + throw exceptions::connection_greeting_error(m_parser->lastLine()); + } + else if (greet->resp_cond_auth()->condition() != IMAPParser::resp_cond_auth::PREAUTH) + { + const authenticationInfos auth = m_auth->requestAuthInfos(); + + // TODO: other authentication methods + + send(true, "LOGIN " + IMAPUtils::quoteString(auth.username()) + + " " + IMAPUtils::quoteString(auth.password()), true); + + utility::auto_ptr resp(m_parser->readResponse()); + + if (resp->isBad()) + { + internalDisconnect(); + throw exceptions::command_error("LOGIN", m_parser->lastLine()); + } + else if (resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + internalDisconnect(); + throw exceptions::authentication_error(m_parser->lastLine()); + } + } + + // Get the hierarchy separator character + initHierarchySeparator(); + + // Switch to state "Authenticated" + setState(STATE_AUTHENTICATED); +} + + +const bool IMAPConnection::isConnected() const +{ + return (m_socket && m_socket->isConnected() && + (m_state == STATE_AUTHENTICATED || m_state == STATE_SELECTED)); +} + + +void IMAPConnection::disconnect() +{ + if (!isConnected()) + throw exceptions::not_connected(); + + internalDisconnect(); +} + + +void IMAPConnection::internalDisconnect() +{ + send(true, "LOGOUT", true); + + m_socket->disconnect(); + + delete (m_socket); + m_socket = NULL; + + delete (m_timeoutHandler); + m_timeoutHandler = NULL; + + m_state = STATE_LOGOUT; +} + + +void IMAPConnection::initHierarchySeparator() +{ + send(true, "LIST \"\" \"\"", true); + + utility::auto_ptr resp(m_parser->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + internalDisconnect(); + throw exceptions::command_error("LIST", m_parser->lastLine(), "bad response"); + } + + const std::vector & respDataList = + resp->continue_req_or_response_data(); + + if (respDataList.size() < 1 || respDataList[0]->response_data() == NULL) + { + internalDisconnect(); + throw exceptions::command_error("LIST", m_parser->lastLine(), "unexpected response"); + } + + const IMAPParser::mailbox_data* mailboxData = + static_cast (respDataList[0]->response_data())-> + mailbox_data(); + + if (mailboxData == NULL || mailboxData->type() != IMAPParser::mailbox_data::LIST) + { + internalDisconnect(); + throw exceptions::command_error("LIST", m_parser->lastLine(), "invalid type"); + } + + if (mailboxData->mailbox_list()->quoted_char() == '\0') + { + internalDisconnect(); + throw exceptions::command_error("LIST", m_parser->lastLine(), "no hierarchy separator"); + } + + m_hierarchySeparator = mailboxData->mailbox_list()->quoted_char(); +} + + +void IMAPConnection::send(bool tag, const string& what, bool end) +{ +#if VMIME_DEBUG + std::ostringstream oss; + + if (tag) + { + ++(*m_tag); + + oss << (string) *m_tag; + oss << " "; + } + + oss << what; + + if (end) + oss << "\r\n"; + + m_socket->send(oss.str()); +#else + if (tag) + { + ++(*m_tag); + + m_socket->send(*m_tag); + m_socket->send(" "); + } + + m_socket->send(what); + + if (end) + { + m_socket->send("\r\n"); + } +#endif +} + + +void IMAPConnection::sendRaw(const char* buffer, const int count) +{ + m_socket->sendRaw(buffer, count); +} + + +IMAPParser::response* IMAPConnection::readResponse(IMAPParser::literalHandler* lh) +{ + return (m_parser->readResponse(lh)); +} + + +} // messaging +} // vmime diff --git a/src/messaging/IMAPConnection.hpp b/src/messaging/IMAPConnection.hpp new file mode 100644 index 00000000..ae92f1e3 --- /dev/null +++ b/src/messaging/IMAPConnection.hpp @@ -0,0 +1,110 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPCONNECTION_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPCONNECTION_HPP_INCLUDED + + +#include "authenticator.hpp" +#include "socket.hpp" +#include "../config.hpp" +#include "timeoutHandler.hpp" + +#include "IMAPParser.hpp" + + +namespace vmime { +namespace messaging { + + +class IMAPTag; +class IMAPStore; + + +class IMAPConnection +{ +public: + + IMAPConnection(IMAPStore* store, authenticator* auth); + ~IMAPConnection(); + + + void connect(); + const bool isConnected() const; + void disconnect(); + + + enum ProtocolStates + { + STATE_NONE, + STATE_NON_AUTHENTICATED, + STATE_AUTHENTICATED, + STATE_SELECTED, + STATE_LOGOUT + }; + + const ProtocolStates state() const { return (m_state); } + void setState(const ProtocolStates state) { m_state = state; } + + + const char hierarchySeparator() const { return (m_hierarchySeparator); } + + + void send(bool tag, const string& what, bool end); + void sendRaw(const char* buffer, const int count); + + IMAPParser::response* readResponse(IMAPParser::literalHandler* lh = NULL); + + + const IMAPTag* tag() const { return (m_tag); } + const IMAPParser* parser() const { return (m_parser); } + + const IMAPStore* store() const { return (m_store); } + IMAPStore* store() { return (m_store); } + +private: + + IMAPStore* m_store; + + authenticator* m_auth; + + socket* m_socket; + + IMAPParser* m_parser; + + IMAPTag* m_tag; + + char m_hierarchySeparator; + + ProtocolStates m_state; + + timeoutHandler* m_timeoutHandler; + + + void internalDisconnect(); + + void initHierarchySeparator(); +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPCONNECTION_HPP_INCLUDED diff --git a/src/messaging/IMAPFolder.cpp b/src/messaging/IMAPFolder.cpp new file mode 100644 index 00000000..717e4817 --- /dev/null +++ b/src/messaging/IMAPFolder.cpp @@ -0,0 +1,1490 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "IMAPFolder.hpp" + +#include "IMAPStore.hpp" +#include "IMAPParser.hpp" +#include "IMAPMessage.hpp" +#include "IMAPUtils.hpp" +#include "IMAPConnection.hpp" + +#include "../exception.hpp" +#include "../utility/smartPtr.hpp" + +#include +#include + + +namespace vmime { +namespace messaging { + + +IMAPFolder::IMAPFolder(const folder::path& path, IMAPStore* store, const int type, const int flags) + : m_store(store), m_connection(m_store->connection()), m_path(path), + m_name(path.last()), m_mode(-1), m_open(false), m_type(type), m_flags(flags), + m_messageCount(0), m_uidValidity(0) +{ + m_store->registerFolder(this); +} + + +IMAPFolder::~IMAPFolder() +{ + if (m_store) + { + if (m_open) + close(false); + + m_store->unregisterFolder(this); + } + else if (m_open) + { + delete (m_connection); + onClose(); + } +} + + +const int IMAPFolder::mode() const +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + return (m_mode); +} + + +const int IMAPFolder::type() +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + // Root folder + if (m_path.empty()) + { + return (TYPE_CONTAINS_FOLDERS); + } + else + { + if (m_type == TYPE_UNDEFINED) + testExistAndGetType(); + + return (m_type); + } +} + + +const int IMAPFolder::flags() +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + // Root folder + if (m_path.empty()) + { + return (FLAG_CHILDREN | FLAG_NO_OPEN); + } + else + { + if (m_flags == FLAG_UNDEFINED) + testExistAndGetType(); + + return (m_flags); + } +} + + +const folder::path::component IMAPFolder::name() const +{ + return (m_name); +} + + +const folder::path IMAPFolder::fullPath() const +{ + return (m_path); +} + + +void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + // Open a connection for this folder + IMAPConnection* connection = + new IMAPConnection(m_store, m_store->oneTimeAuthenticator()); + + try + { + connection->connect(); + + // Emit the "SELECT" command + // + // Example: C: A142 SELECT INBOX + // S: * 172 EXISTS + // S: * 1 RECENT + // S: * OK [UNSEEN 12] Message 12 is first unseen + // S: * OK [UIDVALIDITY 3857529045] UIDs valid + // S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) + // S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited + // S: A142 OK [READ-WRITE] SELECT completed + + std::ostringstream oss; + + if (mode == MODE_READ_ONLY) + oss << "EXAMINE "; + else + oss << "SELECT "; + + oss << IMAPUtils::quoteString(IMAPUtils::pathToString + (connection->hierarchySeparator(), fullPath())); + + connection->send(true, oss.str(), true); + + // Read the response + utility::auto_ptr resp(connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("SELECT", + connection->parser()->lastLine(), "bad response"); + } + + const std::vector & respDataList = + resp->continue_req_or_response_data(); + + for (std::vector ::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("SELECT", + connection->parser()->lastLine(), "invalid response"); + } + + const IMAPParser::response_data* responseData = (*it)->response_data(); + + // OK Untagged responses: UNSEEN, PERMANENTFLAGS, UIDVALIDITY (optional) + if (responseData->resp_cond_state()) + { + const IMAPParser::resp_text_code* code = + responseData->resp_cond_state()->resp_text()->resp_text_code(); + + if (code != NULL) + { + switch (code->type()) + { + case IMAPParser::resp_text_code::UIDVALIDITY: + + m_uidValidity = code->nz_number()->value(); + break; + + default: + + break; + } + } + } + // Untagged responses: FLAGS, EXISTS, RECENT (required) + else if (responseData->mailbox_data()) + { + switch (responseData->mailbox_data()->type()) + { + default: break; + + case IMAPParser::mailbox_data::FLAGS: + { + m_type = IMAPUtils::folderTypeFromFlags + (responseData->mailbox_data()->mailbox_flag_list()); + + m_flags = IMAPUtils::folderFlagsFromFlags + (responseData->mailbox_data()->mailbox_flag_list()); + + break; + } + case IMAPParser::mailbox_data::EXISTS: + { + m_messageCount = responseData->mailbox_data()->number()->value(); + break; + } + case IMAPParser::mailbox_data::RECENT: + { + // TODO + break; + } + + } + } + } + + // Check for access mode (read-only or read-write) + const IMAPParser::resp_text_code* respTextCode = resp->response_done()-> + response_tagged()->resp_cond_state()->resp_text()->resp_text_code(); + + if (respTextCode) + { + const int openMode = + (respTextCode->type() == IMAPParser::resp_text_code::READ_WRITE) + ? MODE_READ_WRITE : MODE_READ_ONLY; + + if (failIfModeIsNotAvailable && + mode == MODE_READ_WRITE && openMode == MODE_READ_ONLY) + { + throw exceptions::operation_not_supported(); + } + } + + + m_connection = connection; + m_open = true; + m_mode = mode; + } + catch (std::exception&) + { + delete (connection); + throw; + } +} + + +void IMAPFolder::close(const bool expunge) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + IMAPConnection* oldConnection = m_connection; + + // Emit the "CLOSE" command to expunge messages marked + // as deleted (this is fastest than "EXPUNGE") + if (expunge) + { + if (m_mode == MODE_READ_ONLY) + throw exceptions::operation_not_supported(); + + oldConnection->send(true, "CLOSE", true); + } + + // Close this folder connection + oldConnection->disconnect(); + + // Now use default store connection + m_connection = m_store->connection(); + + m_open = false; + m_mode = -1; + + m_uidValidity = 0; + + onClose(); + + delete (oldConnection); +} + + +void IMAPFolder::onClose() +{ + for (std::vector ::iterator it = m_messages.begin() ; + it != m_messages.end() ; ++it) + { + (*it)->onFolderClosed(); + } + + m_messages.clear(); +} + + +void IMAPFolder::create(const int type) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (isOpen()) + throw exceptions::illegal_state("Folder is open"); + else if (exists()) + throw exceptions::illegal_state("Folder already exists"); + + // Emit the "CREATE" command + // + // Example: C: A003 CREATE owatagusiam/ + // S: A003 OK CREATE completed + // C: A004 CREATE owatagusiam/blurdybloop + // S: A004 OK CREATE completed + + string mailbox = IMAPUtils::pathToString + (m_connection->hierarchySeparator(), fullPath()); + + if (type & TYPE_CONTAINS_FOLDERS) + mailbox += m_connection->hierarchySeparator(); + + std::ostringstream oss; + oss << "CREATE " << IMAPUtils::quoteString(mailbox); + + m_connection->send(true, oss.str(), true); + + + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("CREATE", + m_connection->parser()->lastLine(), "bad response"); + } + + // Notify folder created + events::folderEvent event(this, events::folderEvent::TYPE_CREATED, m_path, m_path); + notifyFolder(event); +} + + +const bool IMAPFolder::exists() +{ + if (!isOpen() && !m_store) + throw exceptions::illegal_state("Store disconnected"); + + return (testExistAndGetType() != TYPE_UNDEFINED); +} + + +const int IMAPFolder::testExistAndGetType() +{ + m_type = TYPE_UNDEFINED; + + // To test whether a folder exists, we simple list it using + // the "LIST" command, and there should be one unique mailbox + // with this name... + // + // Eg. Test whether '/foo/bar' exists + // + // C: a005 list "" foo/bar + // S: * LIST (\NoSelect) "/" foo/bar + // S: a005 OK LIST completed + // + // ==> OK, exists + // + // Test whether '/foo/bar/zap' exists + // + // C: a005 list "" foo/bar/zap + // S: a005 OK LIST completed + // + // ==> NO, does not exist + + std::ostringstream oss; + oss << "LIST \"\" "; + oss << IMAPUtils::quoteString(IMAPUtils::pathToString + (m_connection->hierarchySeparator(), fullPath())); + + m_connection->send(true, oss.str(), true); + + + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("LIST", + m_connection->parser()->lastLine(), "bad response"); + } + + // Check whether the result mailbox list contains this folder + const std::vector & respDataList = + resp->continue_req_or_response_data(); + + for (std::vector ::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("LIST", + m_connection->parser()->lastLine(), "invalid response"); + } + + const IMAPParser::mailbox_data* mailboxData = + (*it)->response_data()->mailbox_data(); + + // We are only interested in responses of type "LIST" + if (mailboxData != NULL && mailboxData->type() == IMAPParser::mailbox_data::LIST) + { + // Get the folder type/flags at the same time + m_type = IMAPUtils::folderTypeFromFlags + (mailboxData->mailbox_list()->mailbox_flag_list()); + + m_flags = IMAPUtils::folderFlagsFromFlags + (mailboxData->mailbox_list()->mailbox_flag_list()); + } + } + + return (m_type); +} + + +const bool IMAPFolder::isOpen() const +{ + return (m_open); +} + + +message* IMAPFolder::getMessage(const int num) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + if (num < 1 || num > m_messageCount) + throw exceptions::message_not_found(); + + return new IMAPMessage(this, num); +} + + +std::vector IMAPFolder::getMessages(const int from, const int to) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + std::vector v; + + for (int i = from ; i <= to ; ++i) + v.push_back(new IMAPMessage(this, i)); + + return (v); +} + + +std::vector IMAPFolder::getMessages(const std::vector & nums) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + std::vector v; + + for (std::vector ::const_iterator it = nums.begin() ; it != nums.end() ; ++it) + v.push_back(new IMAPMessage(this, *it)); + + return (v); +} + + +const int IMAPFolder::getMessageCount() +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + return (m_messageCount); +} + + +folder* IMAPFolder::getFolder(const folder::path::component& name) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + return new IMAPFolder(m_path / name, m_store); +} + + +std::vector IMAPFolder::getFolders(const bool recursive) +{ + if (!isOpen() && !m_store) + throw exceptions::illegal_state("Store disconnected"); + + // Eg. List folders in '/foo/bar' + // + // C: a005 list "foo/bar" * + // S: * LIST (\NoSelect) "/" foo/bar + // S: * LIST (\NoInferiors) "/" foo/bar/zap + // S: a005 OK LIST completed + + std::ostringstream oss; + oss << "LIST "; + oss << IMAPUtils::quoteString + (IMAPUtils::pathToString(m_connection->hierarchySeparator(), fullPath())); + + if (recursive) + oss << " *"; + else + oss << " %"; + + m_connection->send(true, oss.str(), true); + + + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("LIST", m_connection->parser()->lastLine(), "bad response"); + } + + const std::vector & respDataList = + resp->continue_req_or_response_data(); + + + std::vector v; + + try + { + for (std::vector ::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("LIST", + m_connection->parser()->lastLine(), "invalid response"); + } + + const IMAPParser::mailbox_data* mailboxData = + (*it)->response_data()->mailbox_data(); + + if (mailboxData == NULL || mailboxData->type() != IMAPParser::mailbox_data::LIST) + continue; + + // Get folder path + const class IMAPParser::mailbox* mailbox = + mailboxData->mailbox_list()->mailbox(); + + folder::path path = IMAPUtils::stringToPath + (mailboxData->mailbox_list()->quoted_char(), mailbox->name()); + + if (recursive || m_path.isDirectParentOf(path)) + { + // Append folder to list + const class IMAPParser::mailbox_flag_list* mailbox_flag_list = + mailboxData->mailbox_list()->mailbox_flag_list(); + + v.push_back(new IMAPFolder(path, m_store, + IMAPUtils::folderTypeFromFlags(mailbox_flag_list), + IMAPUtils::folderFlagsFromFlags(mailbox_flag_list))); + } + } + } + catch (std::exception&) + { + for (std::vector ::iterator it = v.begin() ; it != v.end() ; ++it) + delete (*it); + + throw; + } + + return (v); +} + + +void IMAPFolder::fetchMessages(std::vector & msg, const int options, + progressionListener* progress) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + const int total = msg.size(); + int current = 0; + + if (progress) + progress->start(total); + + for (std::vector ::iterator it = msg.begin() ; + it != msg.end() ; ++it) + { + dynamic_cast (*it)->fetch(this, options); + + if (progress) + progress->progress(++current, total); + } + + if (progress) + progress->stop(total); +} + + +void IMAPFolder::fetchMessage(message* msg, const int options) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + dynamic_cast (msg)->fetch(this, options); +} + + +const int IMAPFolder::getFetchCapabilities() const +{ + return (FETCH_ENVELOPE | FETCH_CONTENT_INFO | FETCH_STRUCTURE | + FETCH_FLAGS | FETCH_SIZE | FETCH_FULL_HEADER | FETCH_UID); +} + + +folder* IMAPFolder::getParent() +{ + return (m_path.empty() ? NULL : new IMAPFolder(m_path.parent(), m_store)); +} + + +const class store& IMAPFolder::store() const +{ + return (*m_store); +} + + +class store& IMAPFolder::store() +{ + return (*m_store); +} + + +void IMAPFolder::registerMessage(IMAPMessage* msg) +{ + m_messages.push_back(msg); +} + + +void IMAPFolder::unregisterMessage(IMAPMessage* msg) +{ + std::remove(m_messages.begin(), m_messages.end(), msg); +} + + +void IMAPFolder::onStoreDisconnected() +{ + m_store = NULL; +} + + +void IMAPFolder::deleteMessage(const int num) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Build the request text + std::ostringstream command; + command << "STORE " << num << " +FLAGS.SILENT (\\Deleted)"; + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("STORE", + m_connection->parser()->lastLine(), "bad response"); + } + + // Update local flags + for (std::vector ::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if ((*it)->number() == num && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags |= message::FLAG_DELETED; + } + } + + // Notify message flags changed + std::vector nums; + nums.push_back(num); + + events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, nums); + + notifyMessageChanged(event); +} + + +void IMAPFolder::deleteMessages(const int from, const int to) +{ + if (from < 1 || (to < from && to != -1)) + throw exceptions::invalid_argument(); + + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Build the request text + std::ostringstream command; + command << "STORE " << from << ":"; + + if (to == -1) command << m_messageCount; + else command << to; + + command << " +FLAGS.SILENT (\\Deleted)"; + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("STORE", + m_connection->parser()->lastLine(), "bad response"); + } + + // Update local flags + const int to2 = (to == -1) ? m_messageCount : to; + const int count = to - from + 1; + + for (std::vector ::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if ((*it)->number() >= from && (*it)->number() <= to2 && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags |= message::FLAG_DELETED; + } + } + + // Notify message flags changed + std::vector nums; + nums.resize(count); + + for (int i = from, j = 0 ; i <= to2 ; ++i, ++j) + nums[j] = i; + + events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, nums); + + notifyMessageChanged(event); +} + + +void IMAPFolder::deleteMessages(const std::vector & nums) +{ + if (nums.empty()) + throw exceptions::invalid_argument(); + + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Sort the list of message numbers + std::vector list; + + list.resize(nums.size()); + std::copy(nums.begin(), nums.end(), list.begin()); + + std::sort(list.begin(), list.end()); + + // Build the request text + std::ostringstream command; + command << "STORE "; + command << IMAPUtils::listToSet(list, m_messageCount, true); + command << " +FLAGS.SILENT (\\Deleted)"; + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("STORE", + m_connection->parser()->lastLine(), "bad response"); + } + + // Update local flags + for (std::vector ::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if (std::binary_search(list.begin(), list.end(), (*it)->number())) + { + if ((*it)->m_flags != message::FLAG_UNDEFINED) + (*it)->m_flags |= message::FLAG_DELETED; + } + } + + // Notify message flags changed + events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, list); + + notifyMessageChanged(event); +} + + +void IMAPFolder::setMessageFlags(const int from, const int to, const int flags, const int mode) +{ + if (from < 1 || (to < from && to != -1)) + throw exceptions::invalid_argument(); + + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + std::ostringstream oss; + + if (to == -1) + oss << from << ":*"; + else + oss << from << ":" << to; + + setMessageFlags(oss.str(), flags, mode); + + // Update local flags + const int to2 = (to == -1) ? m_messageCount : to; + const int count = to - from + 1; + + switch (mode) + { + case message::FLAG_MODE_ADD: + { + for (std::vector ::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if ((*it)->number() >= from && (*it)->number() <= to2 && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags |= flags; + } + } + + break; + } + case message::FLAG_MODE_REMOVE: + { + for (std::vector ::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if ((*it)->number() >= from && (*it)->number() <= to2 && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags &= ~flags; + } + } + + break; + } + default: + case message::FLAG_MODE_SET: + { + for (std::vector ::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if ((*it)->number() >= from && (*it)->number() <= to2 && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags = flags; + } + } + + break; + } + + } + + // Notify message flags changed + std::vector nums; + nums.resize(count); + + for (int i = from, j = 0 ; i <= to2 ; ++i, ++j) + nums[j] = i; + + events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, nums); + + notifyMessageChanged(event); +} + + +void IMAPFolder::setMessageFlags(const std::vector & nums, const int flags, const int mode) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Sort the list of message numbers + std::vector list; + + list.resize(nums.size()); + std::copy(nums.begin(), nums.end(), list.begin()); + + std::sort(list.begin(), list.end()); + + // Delegates call + setMessageFlags(IMAPUtils::listToSet(list, m_messageCount, true), flags, mode); + + // Update local flags + switch (mode) + { + case message::FLAG_MODE_ADD: + { + for (std::vector ::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if (std::binary_search(list.begin(), list.end(), (*it)->number()) && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags |= flags; + } + } + + break; + } + case message::FLAG_MODE_REMOVE: + { + for (std::vector ::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if (std::binary_search(list.begin(), list.end(), (*it)->number()) && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags &= ~flags; + } + } + + break; + } + default: + case message::FLAG_MODE_SET: + { + for (std::vector ::iterator it = + m_messages.begin() ; it != m_messages.end() ; ++it) + { + if (std::binary_search(list.begin(), list.end(), (*it)->number()) && + (*it)->m_flags != message::FLAG_UNDEFINED) + { + (*it)->m_flags = flags; + } + } + + break; + } + + } + + // Notify message flags changed + events::messageChangedEvent event(this, events::messageChangedEvent::TYPE_FLAGS, nums); + + notifyMessageChanged(event); +} + + +void IMAPFolder::setMessageFlags(const string& set, const int flags, const int mode) +{ + // Build the request text + std::ostringstream command; + command << "STORE " << set; + + switch (mode) + { + case message::FLAG_MODE_ADD: command << " +FLAGS.SILENT "; break; + case message::FLAG_MODE_REMOVE: command << " -FLAGS.SILENT "; break; + default: + case message::FLAG_MODE_SET: command << " FLAGS.SILENT "; break; + } + + const string flagList = IMAPUtils::messageFlagList(flags); + + if (!flagList.empty()) + { + command << flagList; + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("STORE", + m_connection->parser()->lastLine(), "bad response"); + } + } +} + + +void IMAPFolder::addMessage(vmime::message* msg, const int flags, + vmime::datetime* date, progressionListener* progress) +{ + std::ostringstream oss; + utility::outputStreamAdapter ossAdapter(oss); + + msg->generate(ossAdapter); + + const std::string& str = oss.str(); + utility::inputStreamStringAdapter strAdapter(str); + + addMessage(strAdapter, str.length(), flags, date, progress); +} + + +void IMAPFolder::addMessage(utility::inputStream& is, const int size, const int flags, + vmime::datetime* date, progressionListener* progress) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Build the request text + std::ostringstream command; + command << "APPEND " << IMAPUtils::quoteString(IMAPUtils::pathToString + (m_connection->hierarchySeparator(), fullPath())) << ' '; + + const string flagList = IMAPUtils::messageFlagList(flags); + + if (flags != message::FLAG_UNDEFINED && !flagList.empty()) + { + command << flagList; + command << ' '; + } + + if (date != NULL) + { + command << IMAPUtils::dateTime(*date); + command << ' '; + } + + command << '{' << size << '}'; + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr resp(m_connection->readResponse()); + + bool ok = false; + const std::vector & respList + = resp->continue_req_or_response_data(); + + for (std::vector ::const_iterator + it = respList.begin() ; !ok && (it != respList.end()) ; ++it) + { + if ((*it)->continue_req()) + ok = true; + } + + if (!ok) + { + throw exceptions::command_error("APPEND", + m_connection->parser()->lastLine(), "bad response"); + } + + // Send message data + const int total = size; + int current = 0; + + if (progress) + progress->start(total); + + char buffer[65536]; + + while (!is.eof()) + { + // Read some data from the input stream + const int read = is.read(buffer, sizeof(buffer)); + current += read; + + // Put read data into socket output stream + m_connection->sendRaw(buffer, read); + + // Notify progression + if (progress) + progress->progress(current, total); + } + + m_connection->send(false, "", true); + + if (progress) + progress->stop(total); + + // Get the response + utility::auto_ptr finalResp(m_connection->readResponse()); + + if (finalResp->isBad() || finalResp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("APPEND", + m_connection->parser()->lastLine(), "bad response"); + } + + // Notify message added + std::vector nums; + nums.push_back(m_messageCount + 1); + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + m_messageCount++; + notifyMessageCount(event); +} + + +void IMAPFolder::expunge() +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (m_mode == MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Send the request + m_connection->send(true, "EXPUNGE", true); + + // Get the response + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("EXPUNGE", + m_connection->parser()->lastLine(), "bad response"); + } + + // Update the numbering of the messages + const std::vector & respDataList = + resp->continue_req_or_response_data(); + + std::vector nums; + + for (std::vector ::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("EXPUNGE", + m_connection->parser()->lastLine(), "invalid response"); + } + + const IMAPParser::message_data* messageData = + (*it)->response_data()->message_data(); + + // We are only interested in responses of type "EXPUNGE" + if (messageData == NULL || + messageData->type() != IMAPParser::message_data::EXPUNGE) + { + continue; + } + + const int number = messageData->number(); + + nums.push_back(number); + + for (std::vector ::iterator jt = + m_messages.begin() ; jt != m_messages.end() ; ++jt) + { + if ((*jt)->m_num == number) + (*jt)->m_expunged = true; + else if ((*jt)->m_num > number) + (*jt)->m_num--; + } + } + + // Notify message expunged + events::messageCountEvent event(this, events::messageCountEvent::TYPE_REMOVED, nums); + + m_messageCount -= nums.size(); + notifyMessageCount(event); +} + + +void IMAPFolder::rename(const folder::path& newPath) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (isOpen()) + throw exceptions::illegal_state("Folder open"); + + // Build the request text + std::ostringstream command; + command << "RENAME "; + command << IMAPUtils::quoteString(IMAPUtils::pathToString + (m_connection->hierarchySeparator(), fullPath())) << " "; + command << IMAPUtils::quoteString(IMAPUtils::pathToString + (m_connection->hierarchySeparator(), newPath)); + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("RENAME", + m_connection->parser()->lastLine(), "bad response"); + } + + // Notify folder renamed + folder::path oldPath(m_path); + + m_path = newPath; + m_name = newPath.last(); + + events::folderEvent event(this, events::folderEvent::TYPE_RENAMED, oldPath, newPath); + notifyFolder(event); +} + + +void IMAPFolder::copyMessage(const folder::path& dest, const int num) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + // Construct set + std::ostringstream set; + set << num; + + // Delegate message copy + copyMessages(set.str(), dest); + + // Notify message count changed + std::vector nums; + nums.push_back(num); + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + for (std::list ::iterator it = m_store->m_folders.begin() ; + it != m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == dest) + { + (*it)->m_messageCount++; + (*it)->notifyMessageCount(event); + } + } +} + + +void IMAPFolder::copyMessages(const folder::path& dest, const int from, const int to) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (from < 1 || (to < from && to != -1)) + throw exceptions::invalid_argument(); + + // Construct set + std::ostringstream set; + + if (to == -1) + set << from << ":*"; + else + set << from << ":" << to; + + // Delegate message copy + copyMessages(set.str(), dest); + + // Notify message count changed + const int to2 = (to == -1) ? m_messageCount : to; + const int count = to - from + 1; + + std::vector nums; + nums.resize(count); + + for (int i = from, j = 0 ; i <= to2 ; ++i, ++j) + nums[j] = i; + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + for (std::list ::iterator it = m_store->m_folders.begin() ; + it != m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == dest) + { + (*it)->m_messageCount += count; + (*it)->notifyMessageCount(event); + } + } +} + + +void IMAPFolder::copyMessages(const folder::path& dest, const std::vector & nums) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + // Delegate message copy + copyMessages(IMAPUtils::listToSet(nums, m_messageCount), dest); + + // Notify message count changed + const int count = nums.size(); + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + for (std::list ::iterator it = m_store->m_folders.begin() ; + it != m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == dest) + { + (*it)->m_messageCount += count; + (*it)->notifyMessageCount(event); + } + } +} + + +void IMAPFolder::copyMessages(const string& set, const folder::path& dest) +{ + // Build the request text + std::ostringstream command; + command << "COPY " << set << " "; + command << IMAPUtils::quoteString(IMAPUtils::pathToString + (m_connection->hierarchySeparator(), dest)); + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("COPY", + m_connection->parser()->lastLine(), "bad response"); + } +} + + +void IMAPFolder::status(int& count, int& unseen) +{ + count = 0; + unseen = 0; + + // Build the request text + std::ostringstream command; + command << "STATUS "; + command << IMAPUtils::quoteString(IMAPUtils::pathToString + (m_connection->hierarchySeparator(), fullPath())); + command << "(MESSAGES UNSEEN)"; + + // Send the request + m_store->m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr resp(m_store->m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("STATUS", + m_store->m_connection->parser()->lastLine(), "bad response"); + } + + const std::vector & respDataList = + resp->continue_req_or_response_data(); + + for (std::vector ::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("STATUS", + m_store->m_connection->parser()->lastLine(), "invalid response"); + } + + const IMAPParser::response_data* responseData = (*it)->response_data(); + + if (responseData->mailbox_data() && + responseData->mailbox_data()->type() == IMAPParser::mailbox_data::STATUS) + { + const std::vector & statusList = + responseData->mailbox_data()->status_info_list(); + + for (std::vector ::const_iterator + jt = statusList.begin() ; jt != statusList.end() ; ++jt) + { + switch ((*jt)->status_att()->type()) + { + case IMAPParser::status_att::MESSAGES: + + count = (*jt)->number()->value(); + break; + + case IMAPParser::status_att::UNSEEN: + + unseen = (*jt)->number()->value(); + break; + + default: + + break; + } + } + } + } + + // Notify message count changed (new messages) + if (m_messageCount != count) + { + const int oldCount = m_messageCount; + + m_messageCount = count; + + if (count > oldCount) + { + std::vector nums; + nums.reserve(count - oldCount); + + for (int i = oldCount + 1, j = 0 ; i <= count ; ++i, ++j) + nums[j] = i; + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + for (std::list ::iterator it = m_store->m_folders.begin() ; + it != m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == m_path) + { + (*it)->m_messageCount = count; + (*it)->notifyMessageCount(event); + } + } + } + } +} + + +} // messaging +} // vmime diff --git a/src/messaging/IMAPFolder.hpp b/src/messaging/IMAPFolder.hpp new file mode 100644 index 00000000..bbedb030 --- /dev/null +++ b/src/messaging/IMAPFolder.hpp @@ -0,0 +1,154 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPFOLDER_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPFOLDER_HPP_INCLUDED + + +#include +#include + +#include "../types.hpp" +#include "folder.hpp" + + +namespace vmime { +namespace messaging { + + +class IMAPStore; +class IMAPMessage; +class IMAPConnection; + + +/** IMAP folder implementation. + */ + +class IMAPFolder : public folder +{ +protected: + + friend class IMAPStore; + friend class IMAPMessage; + + + IMAPFolder(const folder::path& path, IMAPStore* store, const int type = TYPE_UNDEFINED, const int flags = FLAG_UNDEFINED); + IMAPFolder(const IMAPFolder&) : folder() { } + + ~IMAPFolder(); + +public: + + const int mode() const; + + const int type(); + + const int flags(); + + const folder::path::component name() const; + const folder::path fullPath() const; + + void open(const int mode, bool failIfModeIsNotAvailable = false); + void close(const bool expunge); + void create(const int type); + + const bool exists(); + + const bool isOpen() const; + + message* getMessage(const int num); + std::vector getMessages(const int from = 1, const int to = -1); + std::vector getMessages(const std::vector & nums); + const int getMessageCount(); + + folder* getFolder(const folder::path::component& name); + std::vector getFolders(const bool recursive = false); + + void rename(const folder::path& newPath); + + void deleteMessage(const int num); + void deleteMessages(const int from = 1, const int to = -1); + void deleteMessages(const std::vector & nums); + + void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET); + void setMessageFlags(const std::vector & nums, const int flags, const int mode = message::FLAG_MODE_SET); + + void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + + void copyMessage(const folder::path& dest, const int num); + void copyMessages(const folder::path& dest, const int from = 1, const int to = -1); + void copyMessages(const folder::path& dest, const std::vector & nums); + + void status(int& count, int& unseen); + + void expunge(); + + folder* getParent(); + + const class store& store() const; + class store& store(); + + + void fetchMessages(std::vector & msg, const int options, progressionListener* progress = NULL); + void fetchMessage(message* msg, const int options); + + const int getFetchCapabilities() const; + +private: + + void registerMessage(IMAPMessage* msg); + void unregisterMessage(IMAPMessage* msg); + + void onStoreDisconnected(); + + void onClose(); + + const int testExistAndGetType(); + + void setMessageFlags(const string& set, const int flags, const int mode); + + void copyMessages(const string& set, const folder::path& dest); + + + IMAPStore* m_store; + IMAPConnection* m_connection; + + folder::path m_path; + folder::path::component m_name; + + int m_mode; + bool m_open; + + int m_type; + int m_flags; + + int m_messageCount; + + int m_uidValidity; + + std::vector m_messages; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPFOLDER_HPP_INCLUDED diff --git a/src/messaging/IMAPMessage.cpp b/src/messaging/IMAPMessage.cpp new file mode 100644 index 00000000..1c2dce7c --- /dev/null +++ b/src/messaging/IMAPMessage.cpp @@ -0,0 +1,843 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "IMAPParser.hpp" +#include "IMAPMessage.hpp" +#include "IMAPFolder.hpp" +#include "IMAPStore.hpp" +#include "IMAPConnection.hpp" +#include "IMAPUtils.hpp" + +#include +#include + + +namespace vmime { +namespace messaging { + + +// +// IMAPheader +// + + +class IMAPheader : public header +{ +public: + + IMAPheader() + { + } + + void parse(const string& str) + { + header::parse(str); + } +}; + + + +// +// IMAPpart +// + +class IMAPpart : public part +{ +private: + + IMAPpart(IMAPpart* parent, const int number, const IMAPParser::body_type_mpart* mpart); + IMAPpart(IMAPpart* parent, const int number, const IMAPParser::body_type_1part* part); + +public: + + const class structure& structure() const; + class structure& structure(); + + const IMAPpart* parent() const { return (m_parent); } + + const mediaType& type() const { return (m_mediaType); } + const int size() const { return (m_size); } + const int number() const { return (m_number); } + + const class header& header() const; + + + static IMAPpart* create(IMAPpart* parent, const int number, const IMAPParser::body* body) + { + if (body->body_type_mpart()) + return new IMAPpart(parent, number, body->body_type_mpart()); + else + return new IMAPpart(parent, number, body->body_type_1part()); + } + + + IMAPheader& getOrCreateHeader() + { + if (m_header != NULL) + return (*m_header); + else + return (*(m_header = new IMAPheader())); + } + +private: + + IMAPstructure* m_structure; + IMAPpart* m_parent; + IMAPheader* m_header; + + int m_number; + int m_size; + mediaType m_mediaType; +}; + + + +// +// IMAPstructure +// + + +class IMAPstructure : public structure +{ +private: + + IMAPstructure() + { + } + +public: + + IMAPstructure(const IMAPParser::body* body) + { + m_parts.push_back(IMAPpart::create(NULL, 1, body)); + } + + IMAPstructure(IMAPpart* parent, const std::vector & list) + { + int number = 1; + + for (std::vector ::const_iterator + it = list.begin() ; it != list.end() ; ++it, ++number) + { + m_parts.push_back(IMAPpart::create(parent, number, *it)); + } + } + + + const part& operator[](const int x) const + { + return (*m_parts[x - 1]); + } + + part& operator[](const int x) + { + return (*m_parts[x - 1]); + } + + const int count() const + { + return (m_parts.size()); + } + + + static IMAPstructure* emptyStructure() + { + return (&m_emptyStructure); + } + +private: + + static IMAPstructure m_emptyStructure; + + std::vector m_parts; +}; + + +IMAPstructure IMAPstructure::m_emptyStructure; + + + +IMAPpart::IMAPpart(IMAPpart* parent, const int number, const IMAPParser::body_type_mpart* mpart) + : m_parent(parent), m_header(NULL), m_number(number), m_size(0) +{ + m_mediaType = vmime::mediaType + ("multipart", mpart->media_subtype()->value()); + + m_structure = new IMAPstructure(this, mpart->list()); +} + + +IMAPpart::IMAPpart(IMAPpart* parent, const int number, const IMAPParser::body_type_1part* part) + : m_parent(parent), m_header(NULL), m_number(number), m_size(0) +{ + if (part->body_type_text()) + { + m_mediaType = vmime::mediaType + ("text", part->body_type_text()-> + media_text()->media_subtype()->value()); + + m_size = part->body_type_text()->body_fields()->body_fld_octets()->value(); + } + else if (part->body_type_msg()) + { + m_mediaType = vmime::mediaType + ("message", part->body_type_msg()-> + media_message()->media_subtype()->value()); + } + else + { + m_mediaType = vmime::mediaType + (part->body_type_basic()->media_basic()->media_type()->value(), + part->body_type_basic()->media_basic()->media_subtype()->value()); + + m_size = part->body_type_basic()->body_fields()->body_fld_octets()->value(); + } + + m_structure = NULL; +} + + +const class header& IMAPpart::header() const +{ + if (m_header == NULL) + throw exceptions::unfetched_object(); + else + return (*m_header); +} + + +const class structure& IMAPpart::structure() const +{ + if (m_structure != NULL) + return (*m_structure); + else + return (*IMAPstructure::emptyStructure()); +} + + +class structure& IMAPpart::structure() +{ + if (m_structure != NULL) + return (*m_structure); + else + return (*IMAPstructure::emptyStructure()); +} + + + +// +// IMAPMessage_literalHandler +// + +class IMAPMessage_literalHandler : public IMAPParser::literalHandler +{ +public: + + IMAPMessage_literalHandler(utility::outputStream& os, progressionListener* progress) + : m_os(os), m_progress(progress) + { + } + + target* targetFor(const IMAPParser::component& comp, const int /* data */) + { + if (typeid(comp) == typeid(IMAPParser::msg_att_item)) + { + const int type = static_cast + (comp).type(); + + if (type == IMAPParser::msg_att_item::BODY_SECTION || + type == IMAPParser::msg_att_item::RFC822_TEXT) + { + return new targetStream(m_progress, m_os); + } + } + + return (NULL); + } + +private: + + utility::outputStream& m_os; + progressionListener* m_progress; +}; + + + +// +// IMAPMessage +// + + +IMAPMessage::IMAPMessage(IMAPFolder* folder, const int num) + : m_folder(folder), m_num(num), m_size(-1), m_flags(FLAG_UNDEFINED), + m_expunged(false), m_header(NULL), m_structure(NULL) +{ + m_folder->registerMessage(this); +} + + +IMAPMessage::~IMAPMessage() +{ + m_folder->unregisterMessage(this); + delete dynamic_cast (m_header); +} + + +void IMAPMessage::onFolderClosed() +{ + m_folder = NULL; +} + + +const int IMAPMessage::number() const +{ + return (m_num); +} + + +const message::uid IMAPMessage::uniqueId() const +{ + return (m_uid); +} + + +const int IMAPMessage::size() const +{ + if (m_size == -1) + throw exceptions::unfetched_object(); + + return (m_size); +} + + +const bool IMAPMessage::isExpunged() const +{ + return (m_expunged); +} + + +const int IMAPMessage::flags() const +{ + if (m_flags == FLAG_UNDEFINED) + throw exceptions::unfetched_object(); + + return (m_flags); +} + + +const class structure& IMAPMessage::structure() const +{ + if (m_structure == NULL) + throw exceptions::unfetched_object(); + + return (*m_structure); +} + + +class structure& IMAPMessage::structure() +{ + if (m_structure == NULL) + throw exceptions::unfetched_object(); + + return (*m_structure); +} + + +const class header& IMAPMessage::header() const +{ + if (m_header == NULL) + throw exceptions::unfetched_object(); + + return (*m_header); +} + + +void IMAPMessage::extract(utility::outputStream& os, progressionListener* progress, + const int start, const int length) const +{ + if (!m_folder) + throw exceptions::folder_not_found(); + + extract(NULL, os, progress, start, length, false); +} + + +void IMAPMessage::extractPart + (const part& p, utility::outputStream& os, progressionListener* progress, + const int start, const int length) const +{ + if (!m_folder) + throw exceptions::folder_not_found(); + + extract(&p, os, progress, start, length, false); +} + + +void IMAPMessage::fetchPartHeader(part& p) +{ + if (!m_folder) + throw exceptions::folder_not_found(); + + std::ostringstream oss; + utility::outputStreamAdapter ossAdapter(oss); + + extract(&p, ossAdapter, NULL, 0, -1, true); + + static_cast (p).getOrCreateHeader().parse(oss.str()); +} + + +void IMAPMessage::extract(const part* p, utility::outputStream& os, progressionListener* progress, + const int start, const int length, const bool headerOnly) const +{ + IMAPMessage_literalHandler literalHandler(os, progress); + + // Construct section identifier + std::ostringstream section; + + if (p != NULL) + { + const IMAPpart* currentPart = static_cast (p); + std::vector numbers; + + numbers.push_back(currentPart->number()); + currentPart = currentPart->parent(); + + while (currentPart != NULL) + { + numbers.push_back(currentPart->number()); + currentPart = currentPart->parent(); + } + + numbers.erase(numbers.end() - 1); + + for (std::vector ::reverse_iterator it = numbers.rbegin() ; it != numbers.rend() ; ++it) + { + if (it != numbers.rbegin()) section << "."; + section << *it; + } + } + + // Build the request text + std::ostringstream command; + + command << "FETCH " << m_num << " BODY["; + command << section.str(); + if (headerOnly) command << ".MIME"; // "MIME" not "HEADER" for parts + command << "]"; + + if (start != 0 || length != -1) + command << "<" << start << "." << length << ">"; + + // Send the request + m_folder->m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr resp + (m_folder->m_connection->readResponse(&literalHandler)); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("FETCH", + m_folder->m_connection->parser()->lastLine(), "bad response"); + } + + + if (!headerOnly) + { + // TODO: update the flags (eg. flag "\Seen" may have been set) + } +} + + +void IMAPMessage::fetch(IMAPFolder* folder, const int options) +{ + if (m_folder != folder) + throw exceptions::folder_not_found(); + + // TODO: optimization: send the request for multiple + // messages at the same time (FETCH x:y) + + // Example: + // C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)]) + // S: * 2 FETCH .... + // S: * 3 FETCH .... + // S: * 4 FETCH .... + // S: A654 OK FETCH completed + + std::vector items; + + if (options & folder::FETCH_SIZE) + items.push_back("RFC822.SIZE"); + + if (options & folder::FETCH_FLAGS) + items.push_back("FLAGS"); + + if (options & folder::FETCH_STRUCTURE) + items.push_back("BODYSTRUCTURE"); + + if (options & folder::FETCH_UID) + items.push_back("UID"); + + if (options & folder::FETCH_FULL_HEADER) + items.push_back("RFC822.HEADER"); + else + { + if (options & folder::FETCH_ENVELOPE) + items.push_back("ENVELOPE"); + + if (options & folder::FETCH_CONTENT_INFO) + items.push_back("BODY[HEADER.FIELDS (CONTENT-TYPE)]"); + } + + // Build the request text + std::ostringstream command; + command << "FETCH " << m_num << " ("; + + for (std::vector ::const_iterator it = items.begin() ; + it != items.end() ; ++it) + { + if (it != items.begin()) command << " "; + command << *it; + } + + command << ")"; + + // Send the request + m_folder->m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr resp(m_folder->m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("FETCH", + m_folder->m_connection->parser()->lastLine(), "bad response"); + } + + const std::vector & respDataList = + resp->continue_req_or_response_data(); + + for (std::vector ::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("FETCH", + m_folder->m_connection->parser()->lastLine(), "invalid response"); + } + + const IMAPParser::message_data* messageData = + (*it)->response_data()->message_data(); + + // We are only interested in responses of type "FETCH" + if (messageData == NULL || messageData->type() != IMAPParser::message_data::FETCH) + continue; + + if (static_cast (messageData->number()) != m_num) + continue; + + // Process fetch response for this message + processFetchResponse(options, messageData->msg_att()); + } +} + + +void IMAPMessage::processFetchResponse + (const int options, const IMAPParser::msg_att* msgAtt) +{ + // Get message attributes + const std::vector atts = + msgAtt->items(); + + int flags = 0; + + for (std::vector ::const_iterator + it = atts.begin() ; it != atts.end() ; ++it) + { + switch ((*it)->type()) + { + case IMAPParser::msg_att_item::FLAGS: + { + flags |= IMAPUtils::messageFlagsFromFlags((*it)->flag_list()); + break; + } + case IMAPParser::msg_att_item::UID: + { + std::ostringstream oss; + oss << m_folder->m_uidValidity << ":" << (*it)->unique_id()->value(); + + m_uid = oss.str(); + break; + } + case IMAPParser::msg_att_item::ENVELOPE: + { + if (!(options & folder::FETCH_FULL_HEADER)) + { + const IMAPParser::envelope* env = (*it)->envelope(); + vmime::header& hdr = getOrCreateHeader(); + + // Date + hdr.fields.Date() = env->env_date()->value(); + + // Subject + text subject; + decodeAndUnfoldText(env->env_subject()->value(), subject); + + hdr.fields.Subject() = subject; + + // From + mailboxList from; + convertAddressList(*(env->env_from()), from); + + if (!from.empty()) + hdr.fields.From() = *(from.begin()); + + // To + mailboxList to; + convertAddressList(*(env->env_to()), to); + + hdr.fields.To() = to; + + // Sender + mailboxList sender; + convertAddressList(*(env->env_sender()), sender); + + if (!sender.empty()) + hdr.fields.Sender() = *(sender.begin()); + + // Reply-to + mailboxList replyTo; + convertAddressList(*(env->env_reply_to()), replyTo); + + if (!replyTo.empty()) + hdr.fields.ReplyTo() = *(replyTo.begin()); + + // Cc + mailboxList cc; + convertAddressList(*(env->env_cc()), cc); + + if (!cc.empty()) + hdr.fields.Cc() = cc; + + // Bcc + mailboxList bcc; + convertAddressList(*(env->env_bcc()), bcc); + + if (!bcc.empty()) + hdr.fields.Bcc() = bcc; + } + + break; + } + case IMAPParser::msg_att_item::BODY_STRUCTURE: + { + delete (m_structure); + m_structure = new IMAPstructure((*it)->body()); + break; + } + case IMAPParser::msg_att_item::RFC822_HEADER: + { + getOrCreateHeader().parse((*it)->nstring()->value()); + break; + } + case IMAPParser::msg_att_item::RFC822_SIZE: + { + m_size = (*it)->number()->value(); + break; + } + case IMAPParser::msg_att_item::BODY_SECTION: + { + if (!(options & folder::FETCH_FULL_HEADER)) + { + if ((*it)->section()->section_text1() && + (*it)->section()->section_text1()->type() + == IMAPParser::section_text::HEADER_FIELDS) + { + IMAPheader tempHeader; + tempHeader.parse((*it)->nstring()->value()); + + vmime::header& hdr = getOrCreateHeader(); + + for (header::const_iterator jt = tempHeader.fields.begin() ; + jt != tempHeader.fields.end() ; ++jt) + { + hdr.fields.append(*jt); + } + } + } + + break; + } + case IMAPParser::msg_att_item::INTERNALDATE: + case IMAPParser::msg_att_item::RFC822: + case IMAPParser::msg_att_item::RFC822_TEXT: + case IMAPParser::msg_att_item::BODY: + { + break; + } + + } + } + + if (options & folder::FETCH_FLAGS) + m_flags = flags; +} + + +IMAPheader& IMAPMessage::getOrCreateHeader() +{ + if (m_header != NULL) + return (*m_header); + else + return (*(m_header = new IMAPheader())); +} + + +void IMAPMessage::convertAddressList + (const IMAPParser::address_list& src, mailboxList& dest) +{ + for (std::vector ::const_iterator + it = src.addresses().begin() ; it != src.addresses().end() ; ++it) + { + const IMAPParser::address& addr = **it; + + text name; + decodeAndUnfoldText(addr.addr_name()->value(), name); + + string email = addr.addr_mailbox()->value() + + "@" + addr.addr_host()->value(); + + dest.append(mailbox(name, email)); + } +} + + +void IMAPMessage::setFlags(const int flags, const int mode) +{ + if (!m_folder) + throw exceptions::folder_not_found(); + else if (m_folder->m_mode == folder::MODE_READ_ONLY) + throw exceptions::illegal_state("Folder is read-only"); + + // Build the request text + std::ostringstream command; + command << "STORE " << m_num; + + switch (mode) + { + case FLAG_MODE_ADD: command << " +FLAGS"; break; + case FLAG_MODE_REMOVE: command << " -FLAGS"; break; + default: + case FLAG_MODE_SET: command << " FLAGS"; break; + } + + if (m_flags == FLAG_UNDEFINED) // Update local flags only if they + command << ".SILENT "; // have been fetched previously + else + command << " "; + + std::vector flagList; + + if (flags & FLAG_REPLIED) flagList.push_back("\\Answered"); + if (flags & FLAG_MARKED) flagList.push_back("\\Flagged"); + if (flags & FLAG_DELETED) flagList.push_back("\\Deleted"); + if (flags & FLAG_SEEN) flagList.push_back("\\Seen"); + + if (!flagList.empty()) + { + command << "("; + + if (flagList.size() >= 2) + { + std::copy(flagList.begin(), flagList.end() - 1, + std::ostream_iterator (command, " ")); + } + + command << *(flagList.end() - 1) << ")"; + + // Send the request + m_folder->m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr resp(m_folder->m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("STORE", + m_folder->m_connection->parser()->lastLine(), "bad response"); + } + + // Update the local flags for this message + if (m_flags != FLAG_UNDEFINED) + { + const std::vector & respDataList = + resp->continue_req_or_response_data(); + + int newFlags = 0; + + for (std::vector ::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + continue; + + const IMAPParser::message_data* messageData = + (*it)->response_data()->message_data(); + + // We are only interested in responses of type "FETCH" + if (messageData == NULL || messageData->type() != IMAPParser::message_data::FETCH) + continue; + + // Get message attributes + const std::vector atts = + messageData->msg_att()->items(); + + for (std::vector ::const_iterator + it = atts.begin() ; it != atts.end() ; ++it) + { + if ((*it)->type() == IMAPParser::msg_att_item::FLAGS) + newFlags |= IMAPUtils::messageFlagsFromFlags((*it)->flag_list()); + } + } + + m_flags = newFlags; + } + + // Notify message flags changed + std::vector nums; + nums.push_back(m_num); + + events::messageChangedEvent event(m_folder, events::messageChangedEvent::TYPE_FLAGS, nums); + + for (std::list ::iterator it = m_folder->m_store->m_folders.begin() ; + it != m_folder->m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == m_folder->m_path) + (*it)->notifyMessageChanged(event); + } + } +} + + +} // messaging +} // vmime diff --git a/src/messaging/IMAPMessage.hpp b/src/messaging/IMAPMessage.hpp new file mode 100644 index 00000000..74c7a36f --- /dev/null +++ b/src/messaging/IMAPMessage.hpp @@ -0,0 +1,108 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPMESSAGE_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPMESSAGE_HPP_INCLUDED + + +#include "message.hpp" +#include "folder.hpp" +#include "../mailboxList.hpp" + + +namespace vmime { +namespace messaging { + + +class IMAPheader; +class IMAPstructure; + + +/** IMAP message implementation. + */ + +class IMAPMessage : public message +{ +protected: + + friend class IMAPFolder; + + IMAPMessage(IMAPFolder* folder, const int num); + IMAPMessage(const IMAPMessage&) : message() { } + + ~IMAPMessage(); + +public: + + const int number() const; + + const uid uniqueId() const; + + const int size() const; + + const bool isExpunged() const; + + const class structure& structure() const; + class structure& structure(); + + const class header& header() const; + + const int flags() const; + void setFlags(const int flags, const int mode = FLAG_MODE_SET); + + void extract(utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + void extractPart(const part& p, utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + + void fetchPartHeader(part& p); + +private: + + void fetch(IMAPFolder* folder, const int options); + + void processFetchResponse(const int options, const IMAPParser::msg_att* msgAtt); + + void extract(const part* p, utility::outputStream& os, progressionListener* progress, const int start, const int length, const bool headerOnly) const; + + + void convertAddressList(const IMAPParser::address_list& src, mailboxList& dest); + + + IMAPheader& getOrCreateHeader(); + + + void onFolderClosed(); + + IMAPFolder* m_folder; + + int m_num; + int m_size; + int m_flags; + bool m_expunged; + uid m_uid; + + class IMAPheader* m_header; + class IMAPstructure* m_structure; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPMESSAGE_HPP_INCLUDED diff --git a/src/messaging/IMAPParser.hpp b/src/messaging/IMAPParser.hpp new file mode 100644 index 00000000..2e4a56ab --- /dev/null +++ b/src/messaging/IMAPParser.hpp @@ -0,0 +1,5075 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPPARSER_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPPARSER_HPP_INCLUDED + + +#include "../base.hpp" +#include "../dateTime.hpp" +#include "../charset.hpp" +#include "../exception.hpp" +#include "../utility/smartPtr.hpp" + +#include "../encoderB64.hpp" +#include "../encoderQP.hpp" + +#include "../platformDependant.hpp" + +#include "progressionListener.hpp" +#include "timeoutHandler.hpp" +#include "socket.hpp" + +#include "IMAPTag.hpp" + +#include +#include + + +//#define DEBUG_RESPONSE 1 + + +#if DEBUG_RESPONSE +# include +#endif + + +namespace vmime { +namespace messaging { + + +#if DEBUG_RESPONSE + static string DEBUG_RESPONSE_level; + static std::vector DEBUG_RESPONSE_components; + +# define DEBUG_ENTER_COMPONENT(x) \ + DEBUG_RESPONSE_components.push_back(x); \ + std::cout << DEBUG_RESPONSE_level \ + << "(" << DEBUG_RESPONSE_level.length() << ") " \ + << (x) << std::endl; +# define DEBUG_FOUND(x, y) \ + std::cout << "FOUND: " << x << ": " << y << std::endl; +#else +# define DEBUG_ENTER_COMPONENT(x) +# define DEBUG_FOUND(x, y) +#endif + + +class IMAPParser +{ +public: + + IMAPParser(IMAPTag* tag, socket* sok, timeoutHandler* _timeoutHandler) + : m_tag(tag), m_socket(sok), m_progress(NULL), + m_literalHandler(NULL), m_timeoutHandler(_timeoutHandler) + { + } + + + const IMAPTag* tag() const + { + return (m_tag); + } + + + const string lastLine() const + { + // Remove blanks and new lines at the end of the line. + string line(m_lastLine); + + string::const_iterator it = line.end(); + int count = 0; + + while (it != line.begin()) + { + const unsigned char c = *(it - 1); + + if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) + break; + + ++count; + --it; + } + + line.resize(line.length() - count); + + return (line); + } + + + + // + // literalHandler : literal content handler + // + + class component; + + class literalHandler + { + public: + + virtual ~literalHandler() { } + + + // Abstract target class + class target + { + protected: + + target(class progressionListener* progress) : m_progress(progress) {} + target(const target&) {} + + public: + + virtual ~target() { } + + + class progressionListener* progressionListener() { return (m_progress); } + + virtual void putData(const string& chunk) = 0; + + private: + + class progressionListener* m_progress; + }; + + + // Target: put in a string + class targetString : public target + { + public: + + targetString(class progressionListener* progress, vmime::string& str) + : target(progress), m_string(str) { } + + const vmime::string& string() const { return (m_string); } + vmime::string& string() { return (m_string); } + + + void putData(const vmime::string& chunk) + { + m_string += chunk; + } + + private: + + vmime::string& m_string; + }; + + + // Target: redirect to an output stream + class targetStream : public target + { + public: + + targetStream(class progressionListener* progress, utility::outputStream& stream) + : target(progress), m_stream(stream) { } + + const utility::outputStream& stream() const { return (m_stream); } + utility::outputStream& stream() { return (m_stream); } + + + void putData(const string& chunk) + { + m_stream.write(chunk.data(), chunk.length()); + } + + private: + + utility::outputStream& m_stream; + }; + + + // Called when the parser needs to know what to do with a literal + // . comp: the component in which we are at this moment + // . data: data specific to the component (may not be used) + // + // Returns : + // . == NULL to put the literal into the response + // . != NULL to redirect the literal to the specified target + + virtual target* targetFor(const component& comp, const int data) = 0; + }; + + + // + // Base class for a terminal or a non-terminal + // + + class component + { + public: + + component() { } + virtual ~component() { } + + virtual void go(IMAPParser& parser, string& line, string::size_type* currentPos) = 0; + + + const string makeResponseLine(const string& comp, const string& line, + const string::size_type pos) + { +#if DEBUG_RESPONSE + if (pos > line.length()) + std::cout << "WARNING: component::makeResponseLine(): pos > line.length()" << std::endl; +#endif + + string result(line.substr(0, pos)); + result += "[^]"; // indicates current parser position + result += line.substr(pos, line.length()); + if (!comp.empty()) result += " [" + comp + "]"; + + return (result); + } + }; + + + + // + // Parse one character + // + + template + class one_char : public component + { + public: + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT(string("one_char <") + C + ">: current='" + ((*currentPos < line.length() ? line[*currentPos] : '?')) + "'"); + + const string::size_type pos = *currentPos; + + if (pos < line.length() && line[pos] == C) + *currentPos = pos + 1; + else + throw exceptions::invalid_response("", makeResponseLine("", line, pos)); + } + }; + + + // + // SPACE ::= + // + + class SPACE : public component + { + public: + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("SPACE"); + + string::size_type pos = *currentPos; + + while (pos < line.length() && (line[pos] == ' ' || line[pos] == '\t')) + ++pos; + + if (pos > *currentPos) + *currentPos = pos; + else + throw exceptions::invalid_response("", makeResponseLine("SPACE", line, pos)); + } + }; + + + // + // CR ::= + // LF ::= + // CRLF ::= CR LF + // + + class CRLF : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("CRLF"); + + string::size_type pos = *currentPos; + + parser.check (line, &pos, true); + + if (pos + 1 < line.length() && + line[pos] == 0x0d && line[pos + 1] == 0x0a) + { + *currentPos = pos + 2; + } + else + { + throw exceptions::invalid_response("", makeResponseLine("CRLF", line, pos)); + } + } + }; + + + // + // SPACE ::= + // CTL ::= + // CHAR ::= + // ATOM_CHAR ::= + // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials + // list_wildcards ::= "%" / "*" + // quoted_specials ::= <"> / "\" + // + // tag ::= 1* (named "xtag") + // + + class xtag : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("tag"); + + string::size_type pos = *currentPos; + + bool end = false; + + string tagString; + tagString.reserve(10); + + while (!end && pos < line.length()) + { + const unsigned char c = line[pos]; + + switch (c) + { + case '+': + case '(': + case ')': + case '{': + case 0x20: // SPACE + case '%': // list_wildcards + case '*': // list_wildcards + case '"': // quoted_specials + case '\\': // quoted_specials + + end = true; + break; + + default: + + if (c <= 0x1f || c >= 0x7f) + end = true; + else + { + tagString += c; + ++pos; + } + + break; + } + } + + if (tagString == (string) *(parser.tag())) + { + *currentPos = pos; + } + else + { + // Invalid tag + throw exceptions::invalid_response("", makeResponseLine("tag", line, pos)); + } + } + }; + + + // + // digit ::= "0" / digit_nz + // digit_nz ::= "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" + // + // number ::= 1*digit + // ;; Unsigned 32-bit integer + // ;; (0 <= n < 4,294,967,296) + // + + class number : public component + { + public: + + number(const bool nonZero = false) + : m_nonZero(nonZero), m_value(0) + { + } + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("number"); + + string::size_type pos = *currentPos; + + bool valid = true; + unsigned int val = 0; + + while (valid && pos < line.length()) + { + const char c = line[pos]; + + if (c >= '0' && c <= '9') + { + val = (val * 10) + (c - '0'); + ++pos; + } + else + { + valid = false; + } + } + + // Check for non-null length (and for non-zero number) + if (!(m_nonZero && val == 0) && pos != *currentPos) + { + m_value = val; + *currentPos = pos; + } + else + { + throw exceptions::invalid_response("", makeResponseLine("number", line, pos)); + } + } + + private: + + const bool m_nonZero; + unsigned int m_value; + + public: + + const unsigned int value() const { return (m_value); } + }; + + + // nz_number ::= digit_nz *digit + // ;; Non-zero unsigned 32-bit integer + // ;; (0 < n < 4,294,967,296) + // + + class nz_number : public number + { + public: + + nz_number() : number(true) + { + } + }; + + + // + // text ::= 1*TEXT_CHAR + // + // CHAR ::= + // TEXT_CHAR ::= + // + + class text : public component + { + public: + + text(bool allow8bits = false, const char except = 0) + : m_allow8bits(allow8bits), m_except(except) + { + } + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("text"); + + string::size_type pos = *currentPos; + string::size_type len = 0; + + if (m_allow8bits) + { + const unsigned char except = m_except; + + for (bool end = false ; !end && pos < line.length() ; ) + { + const unsigned char c = line[pos]; + + if (c == 0x00 || c == 0x0d || c == 0x0a || c == except) + { + end = true; + } + else + { + ++pos; + ++len; + } + } + } + else + { + const unsigned char except = m_except; + + for (bool end = false ; !end && pos < line.length() ; ) + { + const unsigned char c = line[pos]; + + if (c < 0x01 || c > 0x7f || c == 0x0d || c == 0x0a || c == except) + { + end = true; + } + else + { + ++pos; + ++len; + } + } + } + + if (len != 0) + { + m_value.resize(len); + std::copy(line.begin() + *currentPos, line.begin() + pos, m_value.begin()); + + *currentPos = pos; + } + else + { + throw exceptions::invalid_response("", makeResponseLine("text", line, pos)); + } + } + + private: + + string m_value; + const bool m_allow8bits; + const char m_except; + + public: + + const string& value() const { return (m_value); } + }; + + + class text8 : public text + { + public: + + text8() : text(true) + { + } + }; + + + template + class text_except : public text + { + public: + + text_except() : text(false, C) + { + } + }; + + + template + class text8_except : public text + { + public: + + text8_except() : text(true, C) + { + } + }; + + + // + // QUOTED_CHAR ::= / "\" quoted_specials + // quoted_specials ::= <"> / "\" + // TEXT_CHAR ::= + // CHAR ::= + // + + class QUOTED_CHAR : public component + { + public: + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("quoted_char"); + + string::size_type pos = *currentPos; + + const unsigned char c = (pos < line.length() ? line[pos] : 0); + + if (c >= 0x01 && c <= 0x7f && // 0x01 - 0x7f + c != '"' && c != '\\' && // quoted_specials + c != '\r' && c != '\n') // CR and LF + { + m_value = c; + *currentPos = pos + 1; + } + else if (c == '\\' && pos + 1 < line.length() && + (line[pos + 1] == '"' || line[pos + 1] == '\\')) + { + m_value = line[pos + 1]; + *currentPos = pos + 2; + } + else + { + throw exceptions::invalid_response("", makeResponseLine("QUOTED_CHAR", line, pos)); + } + } + + private: + + char m_value; + + public: + + const char value() const { return (m_value); } + }; + + + // + // quoted ::= <"> *QUOTED_CHAR <"> + // QUOTED_CHAR ::= / "\" quoted_specials + // quoted_specials ::= <"> / "\" + // TEXT_CHAR ::= + // CHAR ::= + // + + class quoted_text : public component + { + public: + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("quoted_text"); + + string::size_type pos = *currentPos; + string::size_type len = 0; + bool valid = false; + + m_value.reserve(line.length() - pos); + + for (bool end = false, quoted = false ; !end && pos < line.length() ; ) + { + const unsigned char c = line[pos]; + + if (quoted) + { + if (c == '"' || c == '\\') + m_value += c; + else + { + m_value += '\\'; + m_value += c; + } + + quoted = false; + + ++pos; + ++len; + } + else + { + if (c == '\\') + { + quoted = true; + + ++pos; + ++len; + } + else if (c == '"') + { + valid = true; + end = true; + } + else if (c >= 0x01 && c <= 0x7f && // CHAR + c != 0x0a && c != 0x0d) // CR and LF + { + m_value += c; + + ++pos; + ++len; + } + else + { + valid = false; + end = true; + } + } + } + + if (valid) + { + *currentPos = pos; + } + else + { + throw exceptions::invalid_response("", makeResponseLine("quoted_text", line, pos)); + } + } + + private: + + string m_value; + + public: + + const string& value() const { return (m_value); } + }; + + + // + // nil ::= "NIL" + // + + class NIL : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("NIL"); + + string::size_type pos = *currentPos; + + parser.checkWithArg (line, &pos, "nil"); + + *currentPos = pos; + } + }; + + + // + // string ::= quoted / literal ----> named 'xstring' + // + // nil ::= "NIL" + // quoted ::= <"> *QUOTED_CHAR <"> + // QUOTED_CHAR ::= / "\" quoted_specials + // quoted_specials ::= <"> / "\" + // TEXT_CHAR ::= + // CHAR ::= + // literal ::= "{" number "}" CRLF *CHAR8 + // ;; Number represents the number of CHAR8 octets + // CHAR8 ::= + // + + class xstring : public component + { + public: + + xstring(const bool canBeNIL = false, component* comp = NULL, const int data = 0) + : m_canBeNIL(canBeNIL), m_component(comp), m_data(data) + { + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("string"); + + string::size_type pos = *currentPos; + + if (m_canBeNIL && + parser.checkWithArg (line, &pos, "nil", true)) + { + // NIL + } + else + { + pos = *currentPos; + + // quoted ::= <"> *QUOTED_CHAR <"> + if (parser.check >(line, &pos, true)) + { + utility::auto_ptr text(parser.get (line, &pos)); + parser.check >(line, &pos); + + if (parser.m_literalHandler != NULL) + { + literalHandler::target* target = + parser.m_literalHandler->targetFor(*m_component, m_data); + + if (target != NULL) + { + m_value = "[literal-handler]"; + + const string::size_type length = text->value().length(); + progressionListener* progress = target->progressionListener(); + + if (progress) + { + progress->start(length); + } + + target->putData(text->value()); + + if (progress) + { + progress->progress(length, length); + progress->stop(length); + } + + delete (target); + } + else + { + m_value = text->value(); + } + } + else + { + m_value = text->value(); + } + + DEBUG_FOUND("string[quoted]", ""); + } + // literal ::= "{" number "}" CRLF *CHAR8 + else + { + parser.check >(line, &pos); + + number* num = parser.get (line, &pos); + + const string::size_type length = num->value(); + delete (num); + + parser.check >(line, &pos); + + parser.check (line, &pos); + + + if (parser.m_literalHandler != NULL) + { + literalHandler::target* target = + parser.m_literalHandler->targetFor(*m_component, m_data); + + if (target != NULL) + { + m_value = "[literal-handler]"; + + parser.m_progress = target->progressionListener(); + parser.readLiteral(*target, length); + parser.m_progress = NULL; + + delete (target); + } + else + { + literalHandler::targetString target(NULL, m_value); + parser.readLiteral(target, length); + } + } + else + { + literalHandler::targetString target(NULL, m_value); + parser.readLiteral(target, length); + } + + line += parser.readLine(); + + DEBUG_FOUND("string[literal]", ""); + } + } + + *currentPos = pos; + } + + private: + + bool m_canBeNIL; + string m_value; + + component* m_component; + const int m_data; + + public: + + const string& value() const { return (m_value); } + }; + + + // + // nstring ::= string / nil + // + + class nstring : public xstring + { + public: + + nstring(component* comp = NULL, const int data = 0) + : xstring(true, comp, data) + { + } + }; + + + // + // astring ::= atom / string + // + + class astring : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("astring"); + + string::size_type pos = *currentPos; + + xstring* str = NULL; + + if ((str = parser.get (line, &pos, true))) + { + m_value = str->value(); + delete (str); + } + else + { + atom* at = parser.get (line, &pos); + m_value = at->value(); + delete (at); + } + + *currentPos = pos; + } + + private: + + string m_value; + + public: + + const string& value() const { return (m_value); } + }; + + + // + // atom ::= 1*ATOM_CHAR + // + // ATOM_CHAR ::= + // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards / quoted_specials + // CHAR ::= + // CTL ::= + // list_wildcards ::= "%" / "*" + // quoted_specials ::= <"> / "\" + // SPACE ::= + // + + class atom : public component + { + public: + + void go(IMAPParser& /* parser */, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("atom"); + + string::size_type pos = *currentPos; + string::size_type len = 0; + + for (bool end = false ; !end && pos < line.length() ; ) + { + const unsigned char c = line[pos]; + + switch (c) + { + case '(': + case ')': + case '{': + case 0x20: // SPACE + case '%': // list_wildcards + case '*': // list_wildcards + case '"': // quoted_specials + case '\\': // quoted_specials + + case '[': + case ']': // for "special_atom" + + end = true; + break; + + default: + + if (c <= 0x1f || c >= 0x7f) + end = true; + else + { + ++pos; + ++len; + } + } + } + + if (len != 0) + { + m_value.resize(len); + std::copy(line.begin() + *currentPos, line.begin() + pos, m_value.begin()); + + *currentPos = pos; + } + else + { + throw exceptions::invalid_response("", makeResponseLine("atom", line, pos)); + } + } + + private: + + string m_value; + + public: + + const string& value() const { return (m_value); } + }; + + + // + // special atom (eg. "CAPABILITY", "FLAGS", "STATUS"...) + // + // " Except as noted otherwise, all alphabetic characters are case- + // insensitive. The use of upper or lower case characters to define + // token strings is for editorial clarity only. Implementations MUST + // accept these strings in a case-insensitive fashion. " + // + + class special_atom : public atom + { + public: + + special_atom(const char* str) + : m_string(str) // 'string' must be in lower-case + { + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT(string("special_atom(") + m_string + ")"); + + string::size_type pos = *currentPos; + + atom::go(parser, line, &pos); + + const char* cmp = value().c_str(); + const char* with = m_string; + + bool ok = true; + + while (ok && *cmp && *with) + { + ok = (std::tolower(*cmp, std::locale()) == *with); + + ++cmp; + ++with; + } + + if (!ok || *cmp || *with) + { + throw exceptions::invalid_response("", makeResponseLine(string("special_atom <") + m_string + ">", line, pos)); + } + else + { + *currentPos = pos; + } + } + + private: + + const char* m_string; + }; + + + // + // text_mime2 ::= "=?" "?" "?" "?=" + // ;; Syntax defined in [MIME-HDRS] + // + + class text_mime2 : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("text_mime2"); + + string::size_type pos = *currentPos; + + atom* theCharset = NULL, *theEncoding = NULL; + text* theText = NULL; + + try + { + parser.check >(line, &pos); + + theCharset = parser.get (line, &pos); + + parser.check >(line, &pos); + + theEncoding = parser.get (line, &pos); + + parser.check >(line, &pos); + + theText = parser.get >(line, &pos); + + parser.check >(line, &pos); + parser.check value()[0] == 'Q') + { + // Quoted-printable + theEncoder = new encoderQP; + theEncoder->properties()["rfc2047"] = true; + } + else if (theEncoding->value()[0] == 'b' || theEncoding->value()[0] == 'B') + { + // Base64 + theEncoder = new encoderB64; + } + + if (theEncoder) + { + utility::inputStreamStringAdapter in(theText->value()); + utility::outputStreamStringAdapter out(m_value); + + theEncoder->decode(in, out); + delete (theEncoder); + } + // No decoder available + else + { + m_value = theText->value(); + } + + delete (theEncoding); + delete (theText); + + *currentPos = pos; + } + + private: + + vmime::charset m_charset; + string m_value; + + public: + + const vmime::charset& charset() const { return (m_charset); } + const string& value() const { return (m_value); } + }; + + + // + // flag ::= "\Answered" / "\Flagged" / "\Deleted" / + // "\Seen" / "\Draft" / flag_keyword / flag_extension + // + // flag_extension ::= "\" atom + // ;; Future expansion. Client implementations + // ;; MUST accept flag_extension flags. Server + // ;; implementations MUST NOT generate + // ;; flag_extension flags except as defined by + // ;; future standard or standards-track + // ;; revisions of this specification. + // + // flag_keyword ::= atom + // + + class flag : public component + { + public: + + flag() + : m_flag_keyword(NULL) + { + } + + ~flag() + { + delete (m_flag_keyword); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("flag_keyword"); + + string::size_type pos = *currentPos; + + if (parser.check >(line, &pos, true)) + { + if (parser.check >(line, &pos, true)) + { + m_type = STAR; + } + else + { + atom* at = parser.get (line, &pos); + const string name = toLower(at->value()); + delete (at); + + if (name == "answered") + m_type = ANSWERED; + else if (name == "flagged") + m_type = FLAGGED; + else if (name == "deleted") + m_type = DELETED; + else if (name == "seen") + m_type = SEEN; + else if (name == "draft") + m_type = DRAFT; + else + { + m_type = UNKNOWN; + m_name = name; + } + } + } + else + { + m_flag_keyword = parser.get (line, &pos); + } + + *currentPos = pos; + } + + + enum Type + { + UNKNOWN, + ANSWERED, + FLAGGED, + DELETED, + SEEN, + DRAFT, + STAR // * = custom flags allowed + }; + + private: + + Type m_type; + string m_name; + + IMAPParser::atom* m_flag_keyword; + + public: + + const Type type() const { return (m_type); } + const string& name() const { return (m_name); } + + const IMAPParser::atom* flag_keyword() const { return (m_flag_keyword); } + }; + + + // + // flag_list ::= "(" #flag ")" + // + + class flag_list : public component + { + public: + + ~flag_list() + { + for (std::vector ::iterator it = m_flags.begin() ; + it != m_flags.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("flag_list"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + + while (!parser.check >(line, &pos, true)) + { + m_flags.push_back(parser.get (line, &pos)); + parser.check (line, &pos, true); + } + + *currentPos = pos; + } + + private: + + std::vector m_flags; + + public: + + const std::vector & flags() const { return (m_flags); } + }; + + + // + // mailbox ::= "INBOX" / astring + // ;; INBOX is case-insensitive. All case variants of + // ;; INBOX (e.g. "iNbOx") MUST be interpreted as INBOX + // ;; not as an astring. Refer to section 5.1 for + // ;; further semantic details of mailbox names. + // + + class mailbox : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("mailbox"); + + string::size_type pos = *currentPos; + + if (parser.checkWithArg (line, &pos, "inbox", true)) + { + m_type = INBOX; + m_name = "INBOX"; + } + else + { + m_type = OTHER; + + astring* astr = parser.get (line, &pos); + m_name = astr->value(); + delete (astr); + } + + *currentPos = pos; + } + + + enum Type + { + INBOX, + OTHER + }; + + private: + + Type m_type; + string m_name; + + public: + + const Type type() const { return (m_type); } + const string& name() const { return (m_name); } + }; + + + // + // mailbox_flag := "\Marked" / "\Noinferiors" / + // "\Noselect" / "\Unmarked" / flag_extension + // + + class mailbox_flag : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("mailbox_flag"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + + atom* at = parser.get (line, &pos); + const string name = toLower(at->value()); + delete (at); + + if (name == "marked") + m_type = MARKED; + else if (name == "noinferiors") + m_type = NOINFERIORS; + else if (name == "noselect") + m_type = NOSELECT; + else if (name == "unmarked") + m_type = UNMARKED; + else + { + m_type = UNKNOWN; + m_name = name; + } + + *currentPos = pos; + } + + + enum Type + { + UNKNOWN, + MARKED, + NOINFERIORS, + NOSELECT, + UNMARKED + }; + + private: + + Type m_type; + string m_name; + + public: + + const Type type() const { return (m_type); } + const string& name() const { return (m_name); } + }; + + + // + // mailbox_flag_list ::= "(" #(mailbox_flag) ")" + // + + class mailbox_flag_list : public component + { + public: + + ~mailbox_flag_list() + { + for (std::vector ::iterator it = m_flags.begin() ; + it != m_flags.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("mailbox_flag_list"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + + while (!parser.check >(line, &pos, true)) + { + m_flags.push_back(parser.get (line, &pos)); + parser.check (line, &pos, true); + } + + *currentPos = pos; + } + + private: + + std::vector m_flags; + + public: + + const std::vector & flags() const { return (m_flags); } + }; + + + // + // mailbox_list ::= mailbox_flag_list SPACE + // (<"> QUOTED_CHAR <"> / nil) SPACE mailbox + // + + class mailbox_list : public component + { + public: + + mailbox_list() + : m_mailbox_flag_list(NULL), + m_mailbox(NULL), m_quoted_char('\0') + { + } + + ~mailbox_list() + { + delete (m_mailbox_flag_list); + delete (m_mailbox); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("mailbox_list"); + + string::size_type pos = *currentPos; + + m_mailbox_flag_list = parser.get (line, &pos); + + parser.check (line, &pos); + + if (!parser.check (line, &pos, true)) + { + parser.check >(line, &pos); + + QUOTED_CHAR* qc = parser.get (line, &pos); + m_quoted_char = qc->value(); + delete (qc); + + parser.check >(line, &pos); + } + + parser.check (line, &pos); + + m_mailbox = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::mailbox_flag_list* m_mailbox_flag_list; + IMAPParser::mailbox* m_mailbox; + char m_quoted_char; + + public: + + const IMAPParser::mailbox_flag_list* mailbox_flag_list() const { return (m_mailbox_flag_list); } + const IMAPParser::mailbox* mailbox() const { return (m_mailbox); } + const char quoted_char() const { return (m_quoted_char); } + }; + + + // + // resp_text_code ::= "ALERT" / "PARSE" / + // "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" / + // "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / + // "UIDVALIDITY" SPACE nz_number / + // "UNSEEN" SPACE nz_number / + // atom [SPACE 1*] + + class resp_text_code : public component + { + public: + + resp_text_code() + : m_nz_number(NULL), m_atom(NULL), m_flag_list(NULL), m_text(NULL) + { + } + + ~resp_text_code() + { + delete (m_nz_number); + delete (m_atom); + delete (m_flag_list); + delete (m_text); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("resp_text_code"); + + string::size_type pos = *currentPos; + + // "ALERT" + if (parser.checkWithArg (line, &pos, "alert", true)) + { + m_type = ALERT; + } + // "PARSE" + else if (parser.checkWithArg (line, &pos, "parse", true)) + { + m_type = PARSE; + } + // "PERMANENTFLAGS" SPACE flag_list + else if (parser.checkWithArg (line, &pos, "permanentflags", true)) + { + m_type = PERMANENTFLAGS; + + parser.check (line, &pos); + + m_flag_list = parser.get (line, &pos); + } + // "READ-ONLY" + else if (parser.checkWithArg (line, &pos, "read-only", true)) + { + m_type = READ_ONLY; + } + // "READ-WRITE" + else if (parser.checkWithArg (line, &pos, "read-write", true)) + { + m_type = READ_WRITE; + } + // "TRYCREATE" + else if (parser.checkWithArg (line, &pos, "trycreate", true)) + { + m_type = TRYCREATE; + } + // "UIDVALIDITY" SPACE nz_number + else if (parser.checkWithArg (line, &pos, "uidvalidity", true)) + { + m_type = UIDVALIDITY; + + parser.check (line, &pos); + m_nz_number = parser.get (line, &pos); + } + // "UNSEEN" SPACE nz_number + else if (parser.checkWithArg (line, &pos, "unseen", true)) + { + m_type = UNSEEN; + + parser.check (line, &pos); + m_nz_number = parser.get (line, &pos); + } + // atom [SPACE 1*] + else + { + m_type = OTHER; + + m_atom = parser.get (line, &pos); + + if (parser.check (line, &pos, true)) + m_text = parser.get >(line, &pos); + } + + *currentPos = pos; + } + + + enum Type + { + ALERT, + PARSE, + PERMANENTFLAGS, + READ_ONLY, + READ_WRITE, + TRYCREATE, + UIDVALIDITY, + UNSEEN, + OTHER + }; + + private: + + Type m_type; + + IMAPParser::nz_number* m_nz_number; + IMAPParser::atom* m_atom; + IMAPParser::flag_list* m_flag_list; + IMAPParser::text* m_text; + + public: + + const Type type() const { return (m_type); } + + const IMAPParser::nz_number* nz_number() const { return (m_nz_number); } + const IMAPParser::atom* atom() const { return (m_atom); } + const IMAPParser::flag_list* flag_list() const { return (m_flag_list); } + const IMAPParser::text* text() const { return (m_text); } + }; + + + // + // resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text) + // ;; text SHOULD NOT begin with "[" or "=" + + class resp_text : public component + { + public: + + resp_text() + : m_resp_text_code(NULL) + { + } + + ~resp_text() + { + delete (m_resp_text_code); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("resp_text"); + + string::size_type pos = *currentPos; + + if (parser.check >(line, &pos, true)) + { + m_resp_text_code = parser.get (line, &pos); + + parser.check >(line, &pos); + parser.check (line, &pos); + } + + text_mime2* text1 = parser.get (line, &pos, true); + + if (text1 != NULL) + { + m_text = text1->value(); + delete (text1); + } + else + { + IMAPParser::text* text2 = + parser.get (line, &pos); + + m_text = text2->value(); + delete (text2); + } + + *currentPos = pos; + } + + private: + + IMAPParser::resp_text_code* m_resp_text_code; + string m_text; + + public: + + const IMAPParser::resp_text_code* resp_text_code() const { return (m_resp_text_code); } + const string& text() const { return (m_text); } + }; + + + // + // continue_req ::= "+" SPACE (resp_text / base64) + // + + class continue_req : public component + { + public: + + continue_req() + : m_resp_text(NULL) + { + } + + ~continue_req() + { + delete (m_resp_text); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("continue_req"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + parser.check (line, &pos); + + m_resp_text = parser.get (line, &pos); + + parser.check (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::resp_text* m_resp_text; + + public: + + const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } + }; + + + // + // auth_type ::= atom + // ;; Defined by [IMAP-AUTH] + // + + class auth_type : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("auth_type"); + + atom* at = parser.get (line, currentPos); + m_name = toLower(at->value()); + delete (at); + + if (m_name == "kerberos_v4") + m_type = KERBEROS_V4; + else if (m_name == "gssapi") + m_type = GSSAPI; + else if (m_name == "skey") + m_type = SKEY; + else + m_type = UNKNOWN; + } + + + enum Type + { + UNKNOWN, + + // RFC 1731 - IMAP4 Authentication Mechanisms + KERBEROS_V4, + GSSAPI, + SKEY + }; + + private: + + Type m_type; + string m_name; + + public: + + const Type type() const { return (m_type); } + const string name() const { return (m_name); } + }; + + + // + // status_att ::= "MESSAGES" / "RECENT" / "UIDNEXT" / + // "UIDVALIDITY" / "UNSEEN" + // + + class status_att : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("status_att"); + + string::size_type pos = *currentPos; + + if (parser.checkWithArg (line, &pos, "messages", true)) + { + m_type = MESSAGES; + } + else if (parser.checkWithArg (line, &pos, "recent", true)) + { + m_type = RECENT; + } + else if (parser.checkWithArg (line, &pos, "uidnext", true)) + { + m_type = UIDNEXT; + } + else if (parser.checkWithArg (line, &pos, "uidvalidity", true)) + { + m_type = UIDVALIDITY; + } + else + { + parser.checkWithArg (line, &pos, "unseen"); + m_type = UNSEEN; + } + + *currentPos = pos; + } + + + enum Type + { + MESSAGES, + RECENT, + UIDNEXT, + UIDVALIDITY, + UNSEEN + }; + + private: + + Type m_type; + + public: + + const Type type() const { return (m_type); } + }; + + + // + // capability ::= "AUTH=" auth_type / atom + // ;; New capabilities MUST begin with "X" or be + // ;; registered with IANA as standard or standards-track + // + + class capability : public component + { + public: + + capability() + : m_auth_type(NULL), m_atom(NULL) + { + } + + ~capability() + { + delete (m_auth_type); + delete (m_atom); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("capability"); + + string::size_type pos = *currentPos; + + class atom* at = parser.get (line, &pos); + + string value = at->value(); + const char* str = value.c_str(); + + if ((str[0] == 'a' || str[0] == 'A') && + (str[1] == 'u' || str[1] == 'U') && + (str[2] == 't' || str[2] == 'T') && + (str[3] == 'h' || str[3] == 'H') && + (str[4] == '=')) + { + string::size_type pos = 5; + m_auth_type = parser.get (value, &pos); + delete (at); + } + else + { + m_atom = at; + } + + *currentPos = pos; + } + + private: + + IMAPParser::auth_type* m_auth_type; + IMAPParser::atom* m_atom; + + public: + + const IMAPParser::auth_type* auth_type() const { return (m_auth_type); } + const IMAPParser::atom* atom() const { return (m_atom); } + }; + + + // + // capability_data ::= "CAPABILITY" SPACE [1#capability SPACE] "IMAP4rev1" + // [SPACE 1#capability] + // ;; IMAP4rev1 servers which offer RFC 1730 + // ;; compatibility MUST list "IMAP4" as the first + // ;; capability. + // + + class capability_data : public component + { + public: + + ~capability_data() + { + for (std::vector ::iterator it = m_capabilities.begin() ; + it != m_capabilities.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("capability_data"); + + string::size_type pos = *currentPos; + + parser.checkWithArg (line, &pos, "capability"); + parser.check (line, &pos); + + bool IMAP4rev1 = false; + + for (bool end = false ; !end && !IMAP4rev1 ; ) + { + if (parser.checkWithArg (line, &pos, "imap4rev1", true)) + { + IMAP4rev1 = true; + } + else + { + capability* cap = parser.get (line, &pos); + end = (cap == NULL); + + if (cap) + { + m_capabilities.push_back(cap); + } + } + + parser.check (line, &pos); + } + + + if (parser.check (line, &pos, true)) + { + for (capability* cap = NULL ; + (cap = parser.get (line, &pos)) != NULL ; ) + { + m_capabilities.push_back(cap); + + parser.check (line, &pos); + } + } + + *currentPos = pos; + } + + private: + + std::vector m_capabilities; + + public: + + const std::vector & capabilities() const { return (m_capabilities); } + }; + + + // + // date_day_fixed ::= (SPACE digit) / 2digit + // ;; Fixed-format version of date_day + // + // date_month ::= "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / + // "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" + // + // date_year ::= 4digit + // + // time ::= 2digit ":" 2digit ":" 2digit + // ;; Hours minutes seconds + // + // zone ::= ("+" / "-") 4digit + // ;; Signed four-digit value of hhmm representing + // ;; hours and minutes west of Greenwich (that is, + // ;; (the amount that the given time differs from + // ;; Universal Time). Subtracting the timezone + // ;; from the given time will give the UT form. + // ;; The Universal Time zone is "+0000". + // + // date_time ::= <"> date_day_fixed "-" date_month "-" date_year + // SPACE time SPACE zone <"> + // + + class date_time : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("date_time"); + + string::size_type pos = *currentPos; + + // <"> date_day_fixed "-" date_month "-" date_year + parser.check >(line, &pos); + parser.check (line, &pos, true); + + utility::auto_ptr nd(parser.get (line, &pos)); + + parser.check >(line, &pos); + + utility::auto_ptr amo(parser.get (line, &pos)); + + parser.check >(line, &pos); + + utility::auto_ptr ny(parser.get (line, &pos)); + + parser.check (line, &pos, true); + + // 2digit ":" 2digit ":" 2digit + utility::auto_ptr nh(parser.get (line, &pos)); + + parser.check >(line, &pos); + + utility::auto_ptr nmi(parser.get (line, &pos)); + + parser.check >(line, &pos); + + utility::auto_ptr ns(parser.get (line, &pos)); + + parser.check (line, &pos, true); + + // ("+" / "-") 4digit + int sign = 1; + + if (!(parser.check >(line, &pos, true))) + parser.check >(line, &pos); + + utility::auto_ptr nz(parser.get (line, &pos)); + + parser.check >(line, &pos); + + + m_datetime.hour() = std::min(std::max(nh->value(), 0u), 23u); + m_datetime.minute() = std::min(std::max(nmi->value(), 0u), 59u); + m_datetime.second() = std::min(std::max(ns->value(), 0u), 59u); + + const int zone = static_cast (nz->value()); + const int zh = zone / 100; // hour offset + const int zm = zone % 100; // minute offset + + m_datetime.zone() = ((zh * 60) + zm) * sign; + + m_datetime.day() = std::min(std::max(nd->value(), 1u), 31u); + m_datetime.year() = ny->value(); + + const string month(vmime::toLower(amo->value())); + vmime::datetime::comp_t mon = vmime::datetime::JANUARY; + + if (month.length() >= 3) + { + switch (month[0]) + { + case 'j': + { + switch (month[1]) + { + case 'a': mon = vmime::datetime::JANUARY; break; + case 'u': + { + switch (month[2]) + { + case 'n': mon = vmime::datetime::JUNE; break; + default: mon = vmime::datetime::JULY; break; + } + + break; + } + + } + + break; + } + case 'f': mon = vmime::datetime::FEBRUARY; break; + case 'm': + { + switch (month[2]) + { + case 'r': mon = vmime::datetime::MARCH; break; + default: mon = vmime::datetime::MAY; break; + } + + break; + } + case 'a': + { + switch (month[1]) + { + case 'p': mon = vmime::datetime::APRIL; break; + default: mon = vmime::datetime::AUGUST; break; + } + + break; + } + case 's': mon = vmime::datetime::SEPTEMBER; break; + case 'o': mon = vmime::datetime::OCTOBER; break; + case 'n': mon = vmime::datetime::NOVEMBER; break; + case 'd': mon = vmime::datetime::DECEMBER; break; + } + } + + m_datetime.month() = mon; + + *currentPos = pos; + } + + private: + + vmime::datetime m_datetime; + }; + + + // + // header_fld_name ::= astring + // + + typedef astring header_fld_name; + + + // + // header_list ::= "(" 1#header_fld_name ")" + // + + class header_list : public component + { + public: + + ~header_list() + { + for (std::vector ::iterator it = m_fld_names.begin() ; + it != m_fld_names.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("header_list"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + + while (!parser.check >(line, &pos, true)) + { + m_fld_names.push_back(parser.get (line, &pos)); + parser.check (line, &pos, true); + } + + *currentPos = pos; + } + + private: + + std::vector m_fld_names; + + public: + + const std::vector & fld_names() const { return (m_fld_names); } + }; + + + // + // body_extension ::= nstring / number / "(" 1#body_extension ")" + // ;; Future expansion. Client implementations + // ;; MUST accept body_extension fields. Server + // ;; implementations MUST NOT generate + // ;; body_extension fields except as defined by + // ;; future standard or standards-track + // ;; revisions of this specification. + // + + class body_extension : public component + { + public: + + body_extension() + : m_nstring(NULL), m_number(NULL) + { + } + + ~body_extension() + { + delete (m_nstring); + delete (m_number); + + for (std::vector ::iterator it = m_body_extensions.begin() ; + it != m_body_extensions.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + string::size_type pos = *currentPos; + + if (parser.check >(line, &pos, true)) + { + m_body_extensions.push_back + (parser.get (line, &pos)); + + while (!parser.check >(line, &pos, true)) + m_body_extensions.push_back(parser.get (line, &pos, true)); + } + else + { + if (!(m_nstring = parser.get (line, &pos, true))) + m_number = parser.get (line, &pos); + } + + *currentPos = pos; + } + + private: + + IMAPParser::nstring* m_nstring; + IMAPParser::number* m_number; + + std::vector m_body_extensions; + + public: + + IMAPParser::nstring* nstring() const { return (m_nstring); } + IMAPParser::number* number() const { return (m_number); } + + const std::vector & body_extensions() const { return (m_body_extensions); } + }; + + + // + // section_text ::= "HEADER" / "HEADER.FIELDS" [".NOT"] + // SPACE header_list / "TEXT" / "MIME" + // + + class section_text : public component + { + public: + + section_text() + : m_header_list(NULL) + { + } + + ~section_text() + { + delete (m_header_list); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("section_text"); + + string::size_type pos = *currentPos; + + // "HEADER.FIELDS" [".NOT"] SPACE header_list + const bool b1 = parser.checkWithArg (line, &pos, "header.fields.not", true); + const bool b2 = (b1 ? false : parser.checkWithArg (line, &pos, "header.fields", true)); + + if (b1 || b2) + { + m_type = b1 ? HEADER_FIELDS_NOT : HEADER_FIELDS; + + parser.check (line, &pos); + m_header_list = parser.get (line, &pos); + } + // "HEADER" + else if (parser.checkWithArg (line, &pos, "header", true)) + { + m_type = HEADER; + } + // "MIME" + else if (parser.checkWithArg (line, &pos, "mime", true)) + { + m_type = MIME; + } + // "TEXT" + else + { + m_type = TEXT; + + parser.checkWithArg (line, &pos, "text"); + } + + *currentPos = pos; + } + + + enum Type + { + HEADER, + HEADER_FIELDS, + HEADER_FIELDS_NOT, + MIME, + TEXT + }; + + private: + + Type m_type; + IMAPParser::header_list* m_header_list; + + public: + + const Type type() const { return (m_type); } + const IMAPParser::header_list* header_list() const { return (m_header_list); } + }; + + + // + // section ::= "[" [section_text / (nz_number *["." nz_number] + // ["." (section_text / "MIME")])] "]" + // + + class section : public component + { + public: + + section() + : m_section_text1(NULL), m_section_text2(NULL) + { + } + + ~section() + { + delete (m_section_text1); + delete (m_section_text2); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("section"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + + if (!parser.check >(line, &pos, true)) + { + if (!(m_section_text1 = parser.get (line, &pos, true))) + { + nz_number* num = parser.get (line, &pos); + m_nz_numbers.push_back(num->value()); + delete (num); + + while (parser.check >(line, &pos, true)) + { + if ((num = parser.get (line, &pos, true))) + { + m_nz_numbers.push_back(num->value()); + delete (num); + } + else + { + m_section_text2 = parser.get (line, &pos); + break; + } + } + } + + parser.check >(line, &pos); + } + + *currentPos = pos; + } + + private: + + section_text* m_section_text1; + section_text* m_section_text2; + std::vector m_nz_numbers; + + public: + + const section_text* section_text1() const { return (m_section_text1); } + const section_text* section_text2() const { return (m_section_text2); } + const std::vector & nz_numbers() const { return (m_nz_numbers); } + }; + + + // + // addr_adl ::= nstring + // ;; Holds route from [RFC-822] route-addr if + // ;; non-NIL + // + // addr_host ::= nstring + // ;; NIL indicates [RFC-822] group syntax. + // ;; Otherwise, holds [RFC-822] domain name + // + // addr_mailbox ::= nstring + // ;; NIL indicates end of [RFC-822] group; if + // ;; non-NIL and addr_host is NIL, holds + // ;; [RFC-822] group name. + // ;; Otherwise, holds [RFC-822] local-part + // + // addr_name ::= nstring + // ;; Holds phrase from [RFC-822] mailbox if + // ;; non-NIL + // + // address ::= "(" addr_name SPACE addr_adl SPACE addr_mailbox + // SPACE addr_host ")" + // + + class address : public component + { + public: + + address() + : m_addr_name(NULL), m_addr_adl(NULL), + m_addr_mailbox(NULL), m_addr_host(NULL) + { + } + + ~address() + { + delete (m_addr_name); + delete (m_addr_adl); + delete (m_addr_mailbox); + delete (m_addr_host); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("address"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + m_addr_name = parser.get (line, &pos); + parser.check (line, &pos); + m_addr_adl = parser.get (line, &pos); + parser.check (line, &pos); + m_addr_mailbox = parser.get (line, &pos); + parser.check (line, &pos); + m_addr_host = parser.get (line, &pos); + parser.check >(line, &pos); + + *currentPos = pos; + } + + private: + + nstring* m_addr_name; + nstring* m_addr_adl; + nstring* m_addr_mailbox; + nstring* m_addr_host; + + public: + + nstring* addr_name() const { return (m_addr_name); } + nstring* addr_adl() const { return (m_addr_adl); } + nstring* addr_mailbox() const { return (m_addr_mailbox); } + nstring* addr_host() const { return (m_addr_host); } + }; + + + // + // address_list ::= "(" 1*address ")" / nil + // + + class address_list : public component + { + public: + + ~address_list() + { + for (std::vector ::iterator it = m_addresses.begin() ; + it != m_addresses.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("address_list"); + + string::size_type pos = *currentPos; + + if (!parser.check (line, &pos, true)) + { + parser.check >(line, &pos); + + while (!parser.check >(line, &pos, true)) + { + m_addresses.push_back(parser.get
(line, &pos)); + parser.check (line, &pos, true); + } + } + + *currentPos = pos; + } + + private: + + std::vector m_addresses; + + public: + + const std::vector & addresses() const { return (m_addresses); } + }; + + + // + // env_bcc ::= "(" 1*address ")" / nil + // + + typedef address_list env_bcc; + + + // + // env_cc ::= "(" 1*address ")" / nil + // + + typedef address_list env_cc; + + + // + // env_date ::= nstring + // + + typedef nstring env_date; + + + // + // env_from ::= "(" 1*address ")" / nil + // + + typedef address_list env_from; + + + // + // env_in_reply_to ::= nstring + // + + typedef nstring env_in_reply_to; + + + // + // env_message_id ::= nstring + // + + typedef nstring env_message_id; + + + // + // env_reply_to ::= "(" 1*address ")" / nil + // + + typedef address_list env_reply_to; + + + // + // env_sender ::= "(" 1*address ")" / nil + // + + typedef address_list env_sender; + + + // + // env_subject ::= nstring + // + + typedef nstring env_subject; + + + // + // env_to ::= "(" 1*address ")" / nil + // + + typedef address_list env_to; + + + // + // envelope ::= "(" env_date SPACE env_subject SPACE env_from + // SPACE env_sender SPACE env_reply_to SPACE env_to + // SPACE env_cc SPACE env_bcc SPACE env_in_reply_to + // SPACE env_message_id ")" + // + + class envelope : public component + { + public: + + envelope() + : m_env_date(NULL), m_env_subject(NULL), + m_env_from(NULL), m_env_sender(NULL), m_env_reply_to(NULL), + m_env_to(NULL), m_env_cc(NULL), m_env_bcc(NULL), + m_env_in_reply_to(NULL), m_env_message_id(NULL) + { + } + + ~envelope() + { + delete (m_env_date); + delete (m_env_subject); + delete (m_env_from); + delete (m_env_sender); + delete (m_env_reply_to); + delete (m_env_to); + delete (m_env_cc); + delete (m_env_bcc); + delete (m_env_in_reply_to); + delete (m_env_message_id); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("envelope"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + + m_env_date = parser.get (line, &pos); + parser.check (line, &pos); + + m_env_subject = parser.get (line, &pos); + parser.check (line, &pos); + + m_env_from = parser.get (line, &pos); + parser.check (line, &pos); + + m_env_sender = parser.get (line, &pos); + parser.check (line, &pos); + + m_env_reply_to = parser.get (line, &pos); + parser.check (line, &pos); + + m_env_to = parser.get (line, &pos); + parser.check (line, &pos); + + m_env_cc = parser.get (line, &pos); + parser.check (line, &pos); + + m_env_bcc = parser.get (line, &pos); + parser.check (line, &pos); + + m_env_in_reply_to = parser.get (line, &pos); + parser.check (line, &pos); + + m_env_message_id = parser.get (line, &pos); + + parser.check >(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::env_date* m_env_date; + IMAPParser::env_subject* m_env_subject; + IMAPParser::env_from* m_env_from; + IMAPParser::env_sender* m_env_sender; + IMAPParser::env_reply_to* m_env_reply_to; + IMAPParser::env_to* m_env_to; + IMAPParser::env_cc* m_env_cc; + IMAPParser::env_bcc* m_env_bcc; + IMAPParser::env_in_reply_to* m_env_in_reply_to; + IMAPParser::env_message_id* m_env_message_id; + + public: + + const IMAPParser::env_date* env_date() const { return (m_env_date); } + const IMAPParser::env_subject* env_subject() const { return (m_env_subject); } + const IMAPParser::env_from* env_from() const { return (m_env_from); } + const IMAPParser::env_sender* env_sender() const { return (m_env_sender); } + const IMAPParser::env_reply_to* env_reply_to() const { return (m_env_reply_to); } + const IMAPParser::env_to* env_to() const { return (m_env_to); } + const IMAPParser::env_cc* env_cc() const { return (m_env_cc); } + const IMAPParser::env_bcc* env_bcc() const { return (m_env_bcc); } + const IMAPParser::env_in_reply_to* env_in_reply_to() const { return (m_env_in_reply_to); } + const IMAPParser::env_message_id* env_message_id() const { return (m_env_message_id); } + }; + + + // + // body_fld_desc ::= nstring + // + + typedef nstring body_fld_desc; + + + // + // body_fld_id ::= nstring + // + + typedef nstring body_fld_id; + + + // + // body_fld_md5 ::= nstring + // + + typedef nstring body_fld_md5; + + + // + // body_fld_octets ::= number + // + + typedef number body_fld_octets; + + + // + // body_fld_lines ::= number + // + + typedef number body_fld_lines; + + + // + // body_fld_enc ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/ + // "QUOTED-PRINTABLE") <">) / string + // + + typedef xstring body_fld_enc; + + + // + // body_fld_param_item ::= string SPACE string + // + + class body_fld_param_item : public component + { + public: + + body_fld_param_item() + : m_string1(NULL), m_string2(NULL) + { + } + + ~body_fld_param_item() + { + delete (m_string1); + delete (m_string2); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_fld_param_item"); + + string::size_type pos = *currentPos; + + m_string1 = parser.get (line, &pos); + parser.check (line, &pos); + m_string2 = parser.get (line, &pos); + + DEBUG_FOUND("body_fld_param_item", "<" << m_string1->value() << ", " << m_string2->value() << ">"); + + *currentPos = pos; + } + + private: + + xstring* m_string1; + xstring* m_string2; + + public: + + const xstring* string1() const { return (m_string1); } + const xstring* string2() const { return (m_string2); } + }; + + + // + // body_fld_param ::= "(" 1#(body_fld_param_item) ")" / nil + // + + class body_fld_param : public component + { + public: + + ~body_fld_param() + { + for (std::vector ::iterator it = m_items.begin() ; + it != m_items.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_fld_param"); + + string::size_type pos = *currentPos; + + if (!parser.check (line, &pos, true)) + { + parser.check >(line, &pos); + + m_items.push_back(parser.get (line, &pos)); + + while (!parser.check >(line, &pos, true)) + { + parser.check (line, &pos); + m_items.push_back(parser.get (line, &pos)); + } + } + + *currentPos = pos; + } + + private: + + std::vector m_items; + + public: + + const std::vector & items() const { return (m_items); } + }; + + + // + // body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil + // + + class body_fld_dsp : public component + { + public: + + body_fld_dsp() + : m_string(NULL), m_body_fld_param(NULL) + { + } + + ~body_fld_dsp() + { + delete (m_string); + delete (m_body_fld_param); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_fld_dsp"); + + string::size_type pos = *currentPos; + + if (!parser.check (line, &pos, true)) + { + parser.check >(line, &pos); + m_string = parser.get (line, &pos); + parser.check (line, &pos); + m_body_fld_param = parser.get (line, &pos); + parser.check >(line, &pos); + } + + *currentPos = pos; + } + + private: + + class xstring* m_string; + class body_fld_param* m_body_fld_param; + + public: + + const class xstring* str() const { return (m_string); } + const class body_fld_param* body_fld_param() const { return (m_body_fld_param); } + }; + + + // + // body_fld_lang ::= nstring / "(" 1#string ")" + // + + class body_fld_lang : public component + { + public: + + ~body_fld_lang() + { + for (std::vector ::iterator it = m_strings.begin() ; + it != m_strings.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_fld_lang"); + + string::size_type pos = *currentPos; + + if (parser.check >(line, &pos, true)) + { + m_strings.push_back(parser.get (line, &pos)); + + while (!parser.check >(line, &pos, true)) + m_strings.push_back(parser.get (line, &pos)); + } + else + { + m_strings.push_back(parser.get (line, &pos)); + } + + *currentPos = pos; + } + + private: + + std::vector m_strings; + + public: + + const std::vector & strings() const { return (m_strings); } + }; + + + // + // body_fields ::= body_fld_param SPACE body_fld_id SPACE + // body_fld_desc SPACE body_fld_enc SPACE + // body_fld_octets + // + + class body_fields : public component + { + public: + + body_fields() + : m_body_fld_param(NULL), m_body_fld_id(NULL), + m_body_fld_desc(NULL), m_body_fld_enc(NULL), m_body_fld_octets(NULL) + { + } + + ~body_fields() + { + delete (m_body_fld_param); + delete (m_body_fld_id); + delete (m_body_fld_desc); + delete (m_body_fld_enc); + delete (m_body_fld_octets); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_fields"); + + string::size_type pos = *currentPos; + + m_body_fld_param = parser.get (line, &pos); + parser.check (line, &pos); + m_body_fld_id = parser.get (line, &pos); + parser.check (line, &pos); + m_body_fld_desc = parser.get (line, &pos); + parser.check (line, &pos); + m_body_fld_enc = parser.get (line, &pos); + parser.check (line, &pos); + m_body_fld_octets = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::body_fld_param* m_body_fld_param; + IMAPParser::body_fld_id* m_body_fld_id; + IMAPParser::body_fld_desc* m_body_fld_desc; + IMAPParser::body_fld_enc* m_body_fld_enc; + IMAPParser::body_fld_octets* m_body_fld_octets; + + public: + + const IMAPParser::body_fld_param* body_fld_param() const { return (m_body_fld_param); } + const IMAPParser::body_fld_id* body_fld_id() const { return (m_body_fld_id); } + const IMAPParser::body_fld_desc* body_fld_desc() const { return (m_body_fld_desc); } + const IMAPParser::body_fld_enc* body_fld_enc() const { return (m_body_fld_enc); } + const IMAPParser::body_fld_octets* body_fld_octets() const { return (m_body_fld_octets); } + }; + + + // + // media_subtype ::= string + // ;; Defined in [MIME-IMT] + // + + typedef xstring media_subtype; + + + // + // media_text ::= <"> "TEXT" <"> SPACE media_subtype + // ;; Defined in [MIME-IMT] + // + + class media_text : public component + { + public: + + media_text() + : m_media_subtype(NULL) + { + } + + ~media_text() + { + delete (m_media_subtype); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("media_text"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + parser.checkWithArg (line, &pos, "text"); + parser.check >(line, &pos); + parser.check (line, &pos); + + m_media_subtype = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::media_subtype* m_media_subtype; + + public: + + const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); } + }; + + + // + // media_message ::= <"> "MESSAGE" <"> SPACE <"> "RFC822" <"> + // ;; Defined in [MIME-IMT] + // + + class media_message : public component + { + public: + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("media_message"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + parser.checkWithArg (line, &pos, "message"); + parser.check >(line, &pos); + parser.check (line, &pos); + + //parser.check >(line, &pos); + //parser.checkWithArg (line, &pos, "rfc822"); + //parser.check >(line, &pos); + + m_media_subtype = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::media_subtype* m_media_subtype; + + public: + + const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); } + }; + + + // + // media_basic ::= (<"> ("APPLICATION" / "AUDIO" / "IMAGE" / + // "MESSAGE" / "VIDEO") <">) / string) + // SPACE media_subtype + // ;; Defined in [MIME-IMT] + + class media_basic : public component + { + public: + + media_basic() + : m_media_type(NULL), m_media_subtype(NULL) + { + } + + ~media_basic() + { + delete (m_media_type); + delete (m_media_subtype); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("media_basic"); + + string::size_type pos = *currentPos; + + m_media_type = parser.get (line, &pos); + + parser.check (line, &pos); + + m_media_subtype = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::xstring* m_media_type; + IMAPParser::media_subtype* m_media_subtype; + + public: + + const IMAPParser::xstring* media_type() const { return (m_media_type); } + const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); } + }; + + + // + // body_ext_1part ::= body_fld_md5 [SPACE body_fld_dsp + // [SPACE body_fld_lang + // [SPACE 1#body_extension]]] + // ;; MUST NOT be returned on non-extensible + // ;; "BODY" fetch + // + + class body_ext_1part : public component + { + public: + + body_ext_1part() + : m_body_fld_md5(NULL), m_body_fld_dsp(NULL), m_body_fld_lang(NULL) + { + } + + ~body_ext_1part() + { + delete (m_body_fld_md5); + delete (m_body_fld_dsp); + delete (m_body_fld_lang); + + for (std::vector ::iterator it = m_body_extensions.begin() ; + it != m_body_extensions.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_ext_1part"); + + string::size_type pos = *currentPos; + + m_body_fld_md5 = parser.get (line, &pos); + + // [SPACE body_fld_dsp + if (parser.check (line, &pos, true)) + { + m_body_fld_dsp = parser.get (line, &pos); + + // [SPACE body_fld_lang + if (parser.check (line, &pos, true)) + { + m_body_fld_lang = parser.get (line, &pos); + + // [SPACE 1#body_extension] + if (parser.check (line, &pos, true)) + { + m_body_extensions.push_back + (parser.get (line, &pos)); + + body_extension* ext = NULL; + + while ((ext = parser.get (line, &pos, true)) != NULL) + m_body_extensions.push_back(ext); + } + } + } + + *currentPos = pos; + } + + private: + + IMAPParser::body_fld_md5* m_body_fld_md5; + IMAPParser::body_fld_dsp* m_body_fld_dsp; + IMAPParser::body_fld_lang* m_body_fld_lang; + + std::vector m_body_extensions; + + public: + + const IMAPParser::body_fld_md5* body_fld_md5() const { return (m_body_fld_md5); } + const IMAPParser::body_fld_dsp* body_fld_dsp() const { return (m_body_fld_dsp); } + const IMAPParser::body_fld_lang* body_fld_lang() const { return (m_body_fld_lang); } + + const std::vector body_extensions() const { return (m_body_extensions); } + }; + + + // + // body_ext_mpart ::= body_fld_param + // [SPACE body_fld_dsp SPACE body_fld_lang + // [SPACE 1#body_extension]] + // ;; MUST NOT be returned on non-extensible + // ;; "BODY" fetch + + class body_ext_mpart : public component + { + public: + + body_ext_mpart() + : m_body_fld_param(NULL), m_body_fld_dsp(NULL), m_body_fld_lang(NULL) + { + } + + ~body_ext_mpart() + { + delete (m_body_fld_param); + delete (m_body_fld_dsp); + delete (m_body_fld_lang); + + for (std::vector ::iterator it = m_body_extensions.begin() ; + it != m_body_extensions.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_ext_mpart"); + + string::size_type pos = *currentPos; + + m_body_fld_param = parser.get (line, &pos); + + // [SPACE body_fld_dsp SPACE body_fld_lang [SPACE 1#body_extension]] + if (parser.check (line, &pos, true)) + { + m_body_fld_dsp = parser.get (line, &pos); + parser.check (line, &pos); + m_body_fld_lang = parser.get (line, &pos); + + // [SPACE 1#body_extension] + if (parser.check (line, &pos, true)) + { + m_body_extensions.push_back + (parser.get (line, &pos)); + + body_extension* ext = NULL; + + while ((ext = parser.get (line, &pos, true)) != NULL) + m_body_extensions.push_back(ext); + } + } + + *currentPos = pos; + } + + private: + + IMAPParser::body_fld_param* m_body_fld_param; + IMAPParser::body_fld_dsp* m_body_fld_dsp; + IMAPParser::body_fld_lang* m_body_fld_lang; + + std::vector m_body_extensions; + + public: + + const IMAPParser::body_fld_param* body_fld_param() const { return (m_body_fld_param); } + const IMAPParser::body_fld_dsp* body_fld_dsp() const { return (m_body_fld_dsp); } + const IMAPParser::body_fld_lang* body_fld_lang() const { return (m_body_fld_lang); } + + const std::vector body_extensions() const { return (m_body_extensions); } + }; + + + // + // body_type_basic ::= media_basic SPACE body_fields + // ;; MESSAGE subtype MUST NOT be "RFC822" + // + + class body_type_basic : public component + { + public: + + body_type_basic() + : m_media_basic(NULL), m_body_fields(NULL) + { + } + + ~body_type_basic() + { + delete (m_media_basic); + delete (m_body_fields); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_type_basic"); + + string::size_type pos = *currentPos; + + m_media_basic = parser.get (line, &pos); + parser.check (line, &pos); + m_body_fields = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::media_basic* m_media_basic; + IMAPParser::body_fields* m_body_fields; + + public: + + const IMAPParser::media_basic* media_basic() const { return (m_media_basic); } + const IMAPParser::body_fields* body_fields() const { return (m_body_fields); } + }; + + + // + // body_type_msg ::= media_message SPACE body_fields SPACE envelope + // SPACE body SPACE body_fld_lines + // + + class xbody; + typedef xbody body; + + class body_type_msg : public component + { + public: + + body_type_msg() + : m_media_message(NULL), m_body_fields(NULL), + m_envelope(NULL), m_body(NULL), m_body_fld_lines(NULL) + { + } + + ~body_type_msg() + { + delete (m_media_message); + delete (m_body_fields); + delete (m_envelope); + delete (m_body); + delete (m_body_fld_lines); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_type_msg"); + + string::size_type pos = *currentPos; + + m_media_message = parser.get (line, &pos); + parser.check (line, &pos); + m_body_fields = parser.get (line, &pos); + parser.check (line, &pos); + m_envelope = parser.get (line, &pos); + parser.check (line, &pos); + m_body = parser.get (line, &pos); + parser.check (line, &pos); + m_body_fld_lines = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::media_message* m_media_message; + IMAPParser::body_fields* m_body_fields; + IMAPParser::envelope* m_envelope; + IMAPParser::xbody* m_body; + IMAPParser::body_fld_lines* m_body_fld_lines; + + public: + + const IMAPParser::media_message* media_message() const { return (m_media_message); } + const IMAPParser::body_fields* body_fields() const { return (m_body_fields); } + const IMAPParser::envelope* envelope() const { return (m_envelope); } + const IMAPParser::xbody* body() const { return (m_body); } + const IMAPParser::body_fld_lines* body_fld_lines() const { return (m_body_fld_lines); } + }; + + + // + // body_type_text ::= media_text SPACE body_fields SPACE body_fld_lines + // + + class body_type_text : public component + { + public: + + body_type_text() + : m_media_text(NULL), + m_body_fields(NULL), m_body_fld_lines(NULL) + { + } + + ~body_type_text() + { + delete (m_media_text); + delete (m_body_fields); + delete (m_body_fld_lines); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_type_text"); + + string::size_type pos = *currentPos; + + m_media_text = parser.get (line, &pos); + parser.check (line, &pos); + m_body_fields = parser.get (line, &pos); + parser.check (line, &pos); + m_body_fld_lines = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::media_text* m_media_text; + IMAPParser::body_fields* m_body_fields; + IMAPParser::body_fld_lines* m_body_fld_lines; + + public: + + const IMAPParser::media_text* media_text() const { return (m_media_text); } + const IMAPParser::body_fields* body_fields() const { return (m_body_fields); } + const IMAPParser::body_fld_lines* body_fld_lines() const { return (m_body_fld_lines); } + }; + + + // + // body_type_1part ::= (body_type_basic / body_type_msg / body_type_text) + // [SPACE body_ext_1part] + // + + class body_type_1part : public component + { + public: + + body_type_1part() + : m_body_type_basic(NULL), m_body_type_msg(NULL), + m_body_type_text(NULL), m_body_ext_1part(NULL) + { + } + + ~body_type_1part() + { + delete (m_body_type_basic); + delete (m_body_type_msg); + delete (m_body_type_text); + + delete (m_body_ext_1part); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_type_1part"); + + string::size_type pos = *currentPos; + + if (!(m_body_type_text = parser.get (line, &pos, true))) + if (!(m_body_type_msg = parser.get (line, &pos, true))) + m_body_type_basic = parser.get (line, &pos); + + if (parser.check (line, &pos, true)) + m_body_ext_1part = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::body_type_basic* m_body_type_basic; + IMAPParser::body_type_msg* m_body_type_msg; + IMAPParser::body_type_text* m_body_type_text; + + IMAPParser::body_ext_1part* m_body_ext_1part; + + public: + + const IMAPParser::body_type_basic* body_type_basic() const { return (m_body_type_basic); } + const IMAPParser::body_type_msg* body_type_msg() const { return (m_body_type_msg); } + const IMAPParser::body_type_text* body_type_text() const { return (m_body_type_text); } + + const IMAPParser::body_ext_1part* body_ext_1part() const { return (m_body_ext_1part); } + }; + + + // + // body_type_mpart ::= 1*body SPACE media_subtype + // [SPACE body_ext_mpart] + // + + class body_type_mpart : public component + { + public: + + body_type_mpart() + : m_media_subtype(NULL), m_body_ext_mpart(NULL) + { + } + + ~body_type_mpart() + { + delete (m_media_subtype); + delete (m_body_ext_mpart); + + for (std::vector ::iterator it = m_list.begin() ; + it != m_list.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body_type_mpart"); + + string::size_type pos = *currentPos; + + m_list.push_back(parser.get (line, &pos)); + + for (xbody* b ; (b = parser.get (line, &pos, true)) ; ) + m_list.push_back(b); + + parser.check (line, &pos); + + m_media_subtype = parser.get (line, &pos); + + if (parser.check (line, &pos, true)) + m_body_ext_mpart = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::media_subtype* m_media_subtype; + IMAPParser::body_ext_mpart* m_body_ext_mpart; + + std::vector m_list; + + public: + + const std::vector & list() const { return (m_list); } + + const IMAPParser::media_subtype* media_subtype() const { return (m_media_subtype); } + const IMAPParser::body_ext_mpart* body_ext_mpart() const { return (m_body_ext_mpart); } + }; + + + // + // xbody ::= "(" body_type_1part / body_type_mpart ")" + // + + class xbody : public component + { + public: + + xbody() + : m_body_type_1part(NULL), m_body_type_mpart(NULL) + { + } + + ~xbody() + { + delete (m_body_type_1part); + delete (m_body_type_mpart); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("body"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + + if (!(m_body_type_1part = parser.get (line, &pos, true))) + m_body_type_mpart = parser.get (line, &pos); + + parser.check >(line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::body_type_1part* m_body_type_1part; + IMAPParser::body_type_mpart* m_body_type_mpart; + + public: + + const IMAPParser::body_type_1part* body_type_1part() const { return (m_body_type_1part); } + const IMAPParser::body_type_mpart* body_type_mpart() const { return (m_body_type_mpart); } + }; + + + // + // uniqueid ::= nz_number + // ;; Strictly ascending + // + // msg_att_item ::= "ENVELOPE" SPACE envelope / + // "FLAGS" SPACE "(" #(flag / "\Recent") ")" / + // "INTERNALDATE" SPACE date_time / + // "RFC822" [".HEADER" / ".TEXT"] SPACE nstring / + // "RFC822.SIZE" SPACE number / + // "BODY" ["STRUCTURE"] SPACE body / + // "BODY" section ["<" number ">"] SPACE nstring / + // "UID" SPACE uniqueid + // + + class msg_att_item : public component + { + public: + + msg_att_item() + : m_date_time(NULL), m_number(NULL), m_envelope(NULL), + m_uniqueid(NULL), m_nstring(NULL), m_body(NULL), m_flag_list(NULL) + { + } + + ~msg_att_item() + { + delete (m_date_time); + delete (m_number); + delete (m_envelope); + delete (m_uniqueid); + delete (m_nstring); + delete (m_body); + delete (m_flag_list); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("msg_att_item"); + + string::size_type pos = *currentPos; + + // "ENVELOPE" SPACE envelope + if (parser.checkWithArg (line, &pos, "envelope", true)) + { + m_type = ENVELOPE; + + parser.check (line, &pos); + m_envelope = parser.get (line, &pos); + } + // "FLAGS" SPACE "(" #(flag / "\Recent") ")" + else if (parser.checkWithArg (line, &pos, "flags", true)) + { + m_type = FLAGS; + + parser.check (line, &pos); + + m_flag_list = parser.get (line, &pos); + } + // "INTERNALDATE" SPACE date_time + else if (parser.checkWithArg (line, &pos, "internaldate", true)) + { + m_type = INTERNALDATE; + + parser.check (line, &pos); + m_date_time = parser.get (line, &pos); + } + // "RFC822" ".HEADER" SPACE nstring + else if (parser.checkWithArg (line, &pos, "rfc822.header", true)) + { + m_type = RFC822_HEADER; + + parser.check (line, &pos); + + m_nstring = parser.get (line, &pos); + } + // "RFC822" ".TEXT" SPACE nstring + else if (parser.checkWithArg (line, &pos, "rfc822.text", true)) + { + m_type = RFC822_TEXT; + + parser.check (line, &pos); + + m_nstring = parser.getWithArgs + (line, &pos, this, RFC822_TEXT); + } + // "RFC822.SIZE" SPACE number + else if (parser.checkWithArg (line, &pos, "rfc822.size", true)) + { + m_type = RFC822_SIZE; + + parser.check (line, &pos); + m_number = parser.get (line, &pos); + } + // "RFC822" SPACE nstring + else if (parser.checkWithArg (line, &pos, "rfc822", true)) + { + m_type = RFC822; + + parser.check (line, &pos); + + m_nstring = parser.get (line, &pos); + } + // "BODY" "STRUCTURE" SPACE body + else if (parser.checkWithArg (line, &pos, "bodystructure", true)) + { + m_type = BODY_STRUCTURE; + + parser.check (line, &pos); + + m_body = parser.get (line, &pos); + } + // "BODY" section ["<" number ">"] SPACE nstring + // "BODY" SPACE body + else if (parser.checkWithArg (line, &pos, "body", true)) + { + m_section = parser.get (line, &pos, true); + + // "BODY" section ["<" number ">"] SPACE nstring + if (m_section != NULL) + { + m_type = BODY_SECTION; + + if (parser.check >(line, &pos, true)) + { + m_number = parser.get (line, &pos); + parser.check '> >(line, &pos); + } + + parser.check (line, &pos); + + m_nstring = parser.getWithArgs + (line, &pos, this, BODY_SECTION); + } + // "BODY" SPACE body + else + { + m_type = BODY; + + parser.check (line, &pos); + + m_body = parser.get (line, &pos); + } + } + // "UID" SPACE uniqueid + else + { + m_type = UID; + + parser.checkWithArg (line, &pos, "uid"); + parser.check (line, &pos); + + m_uniqueid = parser.get (line, &pos); + } + + *currentPos = pos; + } + + + enum Type + { + ENVELOPE, + FLAGS, + INTERNALDATE, + RFC822, + RFC822_SIZE, + RFC822_HEADER, + RFC822_TEXT, + BODY, + BODY_SECTION, + BODY_STRUCTURE, + UID + }; + + private: + + Type m_type; + + IMAPParser::date_time* m_date_time; + IMAPParser::number* m_number; + IMAPParser::envelope* m_envelope; + IMAPParser::nz_number* m_uniqueid; + IMAPParser::nstring* m_nstring; + IMAPParser::xbody* m_body; + IMAPParser::flag_list* m_flag_list; + IMAPParser::section* m_section; + + public: + + const Type type() const { return (m_type); } + + const IMAPParser::date_time* date_time() const { return (m_date_time); } + const IMAPParser::number* number() const { return (m_number); } + const IMAPParser::envelope* envelope() const { return (m_envelope); } + const IMAPParser::nz_number* unique_id() const { return (m_uniqueid); } + const IMAPParser::nstring* nstring() const { return (m_nstring); } + const IMAPParser::xbody* body() const { return (m_body); } + const IMAPParser::flag_list* flag_list() const { return (m_flag_list); } + const IMAPParser::section* section() const { return (m_section); } + }; + + + // + // msg_att ::= "(" 1#(msg_att_item) ")" + // + + class msg_att : public component + { + public: + + ~msg_att() + { + for (std::vector ::iterator it = m_items.begin() ; + it != m_items.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("msg_att"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + + m_items.push_back(parser.get (line, &pos)); + + while (!parser.check >(line, &pos, true)) + { + parser.check (line, &pos); + m_items.push_back(parser.get (line, &pos)); + } + + *currentPos = pos; + } + + private: + + std::vector m_items; + + public: + + const std::vector & items() const { return (m_items); } + }; + + + // + // message_data ::= nz_number SPACE ("EXPUNGE" / + // ("FETCH" SPACE msg_att)) + // + + class message_data : public component + { + public: + + message_data() + : m_number(0), m_msg_att(NULL) + { + } + + ~message_data() + { + delete (m_msg_att); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("message_data"); + + string::size_type pos = *currentPos; + + nz_number* num = parser.get (line, &pos); + m_number = num->value(); + delete (num); + + parser.check (line, &pos); + + if (parser.checkWithArg (line, &pos, "expunge", true)) + { + m_type = EXPUNGE; + } + else + { + parser.checkWithArg (line, &pos, "fetch"); + + parser.check (line, &pos); + + m_type = FETCH; + m_msg_att = parser.get (line, &pos); + } + + *currentPos = pos; + } + + + enum Type + { + EXPUNGE, + FETCH + }; + + private: + + Type m_type; + unsigned int m_number; + IMAPParser::msg_att* m_msg_att; + + public: + + const Type type() const { return (m_type); } + const unsigned int number() const { return (m_number); } + const IMAPParser::msg_att* msg_att() const { return (m_msg_att); } + }; + + + // + // resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text + // ;; Status condition + // + + class resp_cond_state : public component + { + public: + + resp_cond_state() + : m_resp_text(NULL), m_status(BAD) + { + } + + ~resp_cond_state() + { + delete (m_resp_text); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("resp_cond_state"); + + string::size_type pos = *currentPos; + + if (parser.checkWithArg (line, &pos, "ok", true)) + { + m_status = OK; + } + else if (parser.checkWithArg (line, &pos, "no", true)) + { + m_status = NO; + } + else + { + parser.checkWithArg (line, &pos, "bad"); + m_status = BAD; + } + + parser.check (line, &pos); + + m_resp_text = parser.get (line, &pos); + + *currentPos = pos; + } + + + enum Status + { + OK, + NO, + BAD + }; + + private: + + IMAPParser::resp_text* m_resp_text; + Status m_status; + + public: + + const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } + const Status status() const { return (m_status); } + }; + + + // + // resp_cond_bye ::= "BYE" SPACE resp_text + // + + class resp_cond_bye : public component + { + public: + + resp_cond_bye() + : m_resp_text(NULL) + { + } + + ~resp_cond_bye() + { + delete (m_resp_text); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("resp_cond_bye"); + + string::size_type pos = *currentPos; + + parser.checkWithArg (line, &pos, "bye"); + + parser.check (line, &pos); + + m_resp_text = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::resp_text* m_resp_text; + + public: + + const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } + }; + + + // + // resp_cond_auth ::= ("OK" / "PREAUTH") SPACE resp_text + // ;; Authentication condition + // + + class resp_cond_auth : public component + { + public: + + resp_cond_auth() + : m_resp_text(NULL) + { + } + + ~resp_cond_auth() + { + delete (m_resp_text); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("resp_cond_auth"); + + string::size_type pos = *currentPos; + + if (parser.checkWithArg (line, &pos, "ok", true)) + { + m_cond = OK; + } + else + { + parser.checkWithArg (line, &pos, "preauth"); + + m_cond = PREAUTH; + } + + parser.check (line, &pos); + + m_resp_text = parser.get (line, &pos); + + *currentPos = pos; + } + + + enum Condition + { + OK, + PREAUTH + }; + + private: + + Condition m_cond; + IMAPParser::resp_text* m_resp_text; + + public: + + const Condition condition() const { return (m_cond); } + const IMAPParser::resp_text* resp_text() const { return (m_resp_text); } + }; + + + // + // status_info ::= status_att SPACE number + // + + class status_info : public component + { + public: + + status_info() + : m_status_att(NULL), m_number(NULL) + { + } + + ~status_info() + { + delete (m_status_att); + delete (m_number); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("status_info"); + + string::size_type pos = *currentPos; + + m_status_att = parser.get (line, &pos); + parser.check (line, &pos); + m_number = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::status_att* m_status_att; + IMAPParser::number* m_number; + + public: + + const IMAPParser::status_att* status_att() const { return (m_status_att); } + const IMAPParser::number* number() const { return (m_number); } + }; + + + // + // mailbox_data ::= "FLAGS" SPACE mailbox_flag_list / + // "LIST" SPACE mailbox_list / + // "LSUB" SPACE mailbox_list / + // "MAILBOX" SPACE text / + // "SEARCH" [SPACE 1#nz_number] / + // "STATUS" SPACE mailbox SPACE + // "(" #::iterator it = m_search_nz_number_list.begin() ; + it != m_search_nz_number_list.end() ; ++it) + { + delete (*it); + } + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("mailbox_data"); + + string::size_type pos = *currentPos; + + m_number = parser.get (line, &pos, true); + + if (m_number) + { + parser.check (line, &pos); + + if (parser.checkWithArg (line, &pos, "exists", true)) + { + m_type = EXISTS; + } + else + { + parser.checkWithArg (line, &pos, "recent"); + + m_type = RECENT; + } + } + else + { + // "FLAGS" SPACE mailbox_flag_list + if (parser.checkWithArg (line, &pos, "flags", true)) + { + parser.check (line, &pos); + + m_mailbox_flag_list = parser.get (line, &pos); + + m_type = FLAGS; + } + // "LIST" SPACE mailbox_list + else if (parser.checkWithArg (line, &pos, "list", true)) + { + parser.check (line, &pos); + + m_mailbox_list = parser.get (line, &pos); + + m_type = LIST; + } + // "LSUB" SPACE mailbox_list + else if (parser.checkWithArg (line, &pos, "lsub", true)) + { + parser.check (line, &pos); + + m_mailbox_list = parser.get (line, &pos); + + m_type = LSUB; + } + // "MAILBOX" SPACE text + else if (parser.checkWithArg (line, &pos, "mailbox", true)) + { + parser.check (line, &pos); + + m_text = parser.get (line, &pos); + + m_type = MAILBOX; + } + // "SEARCH" [SPACE 1#nz_number] + else if (parser.checkWithArg (line, &pos, "search", true)) + { + if (parser.check (line, &pos, true)) + { + m_search_nz_number_list.push_back + (parser.get (line, &pos)); + + while (parser.check (line, &pos, true)) + { + m_search_nz_number_list.push_back + (parser.get (line, &pos)); + } + } + + m_type = SEARCH; + } + // "STATUS" SPACE mailbox SPACE + // "(" #(line, &pos, "status"); + parser.check (line, &pos); + + m_mailbox = parser.get (line, &pos); + + parser.check (line, &pos); + parser.check >(line, &pos); + + m_status_info_list.push_back(parser.get (line, &pos)); + + while (!parser.check >(line, &pos, true)) + m_status_info_list.push_back(parser.get (line, &pos)); + + m_type = STATUS; + } + } + + *currentPos = pos; + } + + + enum Type + { + FLAGS, + LIST, + LSUB, + MAILBOX, + SEARCH, + STATUS, + EXISTS, + RECENT + }; + + private: + + Type m_type; + + IMAPParser::number* m_number; + IMAPParser::mailbox_flag_list* m_mailbox_flag_list; + IMAPParser::mailbox_list* m_mailbox_list; + IMAPParser::mailbox* m_mailbox; + IMAPParser::text* m_text; + std::vector m_search_nz_number_list; + std::vector m_status_info_list; + + public: + + const Type type() const { return (m_type); } + + const IMAPParser::number* number() const { return (m_number); } + const IMAPParser::mailbox_flag_list* mailbox_flag_list() const { return (m_mailbox_flag_list); } + const IMAPParser::mailbox_list* mailbox_list() const { return (m_mailbox_list); } + const IMAPParser::mailbox* mailbox() const { return (m_mailbox); } + const IMAPParser::text* text() const { return (m_text); } + const std::vector & search_nz_number_list() const { return (m_search_nz_number_list); } + const std::vector & status_info_list() const { return (m_status_info_list); } + }; + + + // + // response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye / + // mailbox_data / message_data / capability_data) CRLF + // + + class response_data : public component + { + public: + + response_data() + : m_resp_cond_state(NULL), m_resp_cond_bye(NULL), + m_mailbox_data(NULL), m_message_data(NULL), m_capability_data(NULL) + { + } + + ~response_data() + { + delete (m_resp_cond_state); + delete (m_resp_cond_bye); + delete (m_mailbox_data); + delete (m_message_data); + delete (m_capability_data); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("response_data"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + parser.check (line, &pos); + + if (!(m_resp_cond_state = parser.get (line, &pos, true))) + if (!(m_resp_cond_bye = parser.get (line, &pos, true))) + if (!(m_mailbox_data = parser.get (line, &pos, true))) + if (!(m_message_data = parser.get (line, &pos, true))) + m_capability_data = parser.get (line, &pos); + + parser.check (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::resp_cond_state* m_resp_cond_state; + IMAPParser::resp_cond_bye* m_resp_cond_bye; + IMAPParser::mailbox_data* m_mailbox_data; + IMAPParser::message_data* m_message_data; + IMAPParser::capability_data* m_capability_data; + + public: + + const IMAPParser::resp_cond_state* resp_cond_state() const { return (m_resp_cond_state); } + const IMAPParser::resp_cond_bye* resp_cond_bye() const { return (m_resp_cond_bye); } + const IMAPParser::mailbox_data* mailbox_data() const { return (m_mailbox_data); } + const IMAPParser::message_data* message_data() const { return (m_message_data); } + const IMAPParser::capability_data* capability_data() const { return (m_capability_data); } + }; + + + class continue_req_or_response_data : public component + { + public: + + continue_req_or_response_data() + : m_continue_req(NULL), m_response_data(NULL) + { + } + + ~continue_req_or_response_data() + { + delete (m_continue_req); + delete (m_response_data); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("continue_req_or_response_data"); + + string::size_type pos = *currentPos; + + if (!(m_continue_req = parser.get (line, &pos, true))) + m_response_data = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::continue_req* m_continue_req; + IMAPParser::response_data* m_response_data; + + public: + + const IMAPParser::continue_req* continue_req() const { return (m_continue_req); } + const IMAPParser::response_data* response_data() const { return (m_response_data); } + }; + + + // + // response_fatal ::= "*" SPACE resp_cond_bye CRLF + // ;; Server closes connection immediately + // + + class response_fatal : public component + { + public: + + response_fatal() + : m_resp_cond_bye(NULL) + { + } + + ~response_fatal() + { + delete (m_resp_cond_bye); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("response_fatal"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + parser.check (line, &pos); + + m_resp_cond_bye = parser.get (line, &pos); + + parser.check (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::resp_cond_bye* m_resp_cond_bye; + + public: + + const IMAPParser::resp_cond_bye* resp_cond_bye() const { return (m_resp_cond_bye); } + }; + + + // + // response_tagged ::= tag SPACE resp_cond_state CRLF + // + + class response_tagged : public component + { + public: + + response_tagged() + : m_resp_cond_state(NULL) + { + } + + ~response_tagged() + { + delete (m_resp_cond_state); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("response_tagged"); + + string::size_type pos = *currentPos; + + parser.check (line, &pos); + parser.check (line, &pos); + m_resp_cond_state = parser.get (line, &pos); + parser.check (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::resp_cond_state* m_resp_cond_state; + + public: + + const IMAPParser::resp_cond_state* resp_cond_state() const { return (m_resp_cond_state); } + }; + + + // + // response_done ::= response_tagged / response_fatal + // + + class response_done : public component + { + public: + + response_done() + : m_response_tagged(NULL), m_response_fatal(NULL) + { + } + + ~response_done() + { + delete (m_response_tagged); + delete (m_response_fatal); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("response_done"); + + string::size_type pos = *currentPos; + + if (!(m_response_tagged = parser.get (line, &pos, true))) + m_response_fatal = parser.get (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::response_tagged* m_response_tagged; + IMAPParser::response_fatal* m_response_fatal; + + public: + + const IMAPParser::response_tagged* response_tagged() const { return (m_response_tagged); } + const IMAPParser::response_fatal* response_fatal() const { return (m_response_fatal); } + }; + + + // + // response ::= *(continue_req / response_data) response_done + // + + class response : public component + { + public: + + response() + : m_response_done(NULL) + { + } + + ~response() + { + for (std::vector ::iterator + it = m_continue_req_or_response_data.begin() ; + it != m_continue_req_or_response_data.end() ; ++it) + { + delete (*it); + } + + delete (m_response_done); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("response"); + + string::size_type pos = *currentPos; + string curLine = line; + bool partial = false; // partial response + + IMAPParser::continue_req_or_response_data* resp = NULL; + + while ((resp = parser.get (curLine, &pos, true)) != NULL) + { + m_continue_req_or_response_data.push_back(resp); + + // Partial response (continue_req) + if (resp->continue_req()) + { + partial = true; + break; + } + + // We have read a CRLF, read another line + curLine = parser.readLine(); + pos = 0; + } + + if (!partial) + m_response_done = parser.get (curLine, &pos); + + *currentPos = pos; + } + + + const bool isBad() const + { + if (!response_done()) // incomplete (partial) response + return (true); + + if (response_done()->response_fatal()) + return (true); + + if (response_done()->response_tagged()->resp_cond_state()-> + status() == IMAPParser::resp_cond_state::BAD) + { + return (true); + } + + return (false); + } + + private: + + std::vector m_continue_req_or_response_data; + IMAPParser::response_done* m_response_done; + + public: + + const std::vector & continue_req_or_response_data() const { return (m_continue_req_or_response_data); } + const IMAPParser::response_done* response_done() const { return (m_response_done); } + }; + + + // + // greeting ::= "*" SPACE (resp_cond_auth / resp_cond_bye) CRLF + // + + class greeting : public component + { + public: + + greeting() + : m_resp_cond_auth(NULL), m_resp_cond_bye(NULL) + { + } + + ~greeting() + { + delete (m_resp_cond_auth); + delete (m_resp_cond_bye); + } + + void go(IMAPParser& parser, string& line, string::size_type* currentPos) + { + DEBUG_ENTER_COMPONENT("greeting"); + + string::size_type pos = *currentPos; + + parser.check >(line, &pos); + parser.check (line, &pos); + + if (!(m_resp_cond_auth = parser.get (line, &pos, true))) + m_resp_cond_bye = parser.get (line, &pos); + + parser.check (line, &pos); + + *currentPos = pos; + } + + private: + + IMAPParser::resp_cond_auth* m_resp_cond_auth; + IMAPParser::resp_cond_bye* m_resp_cond_bye; + + public: + + const IMAPParser::resp_cond_auth* resp_cond_auth() const { return (m_resp_cond_auth); } + const IMAPParser::resp_cond_bye* resp_cond_bye() const { return (m_resp_cond_bye); } + }; + + + + // + // The main functions used to parse a response + // + + response* readResponse(literalHandler* lh = NULL) + { + string::size_type pos = 0; + string line = readLine(); + + m_literalHandler = lh; + response* resp = get (line, &pos); + m_literalHandler = NULL; + + return (resp); + } + + + greeting* readGreeting() + { + string::size_type pos = 0; + string line = readLine(); + + return get (line, &pos); + } + + + // + // Get a token and advance + // + + template + TYPE* get(string& line, string::size_type* currentPos, + const bool noThrow = false) + { + component* resp = new TYPE; + return internalGet (resp, line, currentPos, noThrow); + } + + + template + TYPE* getWithArgs(string& line, string::size_type* currentPos, + ARG1_TYPE arg1, ARG2_TYPE arg2, const bool noThrow = false) + { + component* resp = new TYPE(arg1, arg2); + return internalGet (resp, line, currentPos, noThrow); + } + + +private: + + template + TYPE* internalGet(component* resp, string& line, string::size_type* currentPos, + const bool noThrow = false) + { +#if DEBUG_RESPONSE + DEBUG_RESPONSE_level += " "; +#endif + + try + { + resp->go(*this, line, currentPos); + +#if DEBUG_RESPONSE + std::cout << DEBUG_RESPONSE_level << "SUCCESS! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; + + DEBUG_RESPONSE_level.erase(DEBUG_RESPONSE_level.begin() + DEBUG_RESPONSE_level.length() - 1); + DEBUG_RESPONSE_components.pop_back(); +#endif + } + catch (...) + { +#if DEBUG_RESPONSE + std::cout << DEBUG_RESPONSE_level << "FAILED! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; + + DEBUG_RESPONSE_level.erase(DEBUG_RESPONSE_level.begin() + DEBUG_RESPONSE_level.length() - 1); + DEBUG_RESPONSE_components.pop_back(); +#endif + + delete (resp); + if (!noThrow) throw; + return (NULL); + } + + return static_cast (resp); + } + + +public: + + // + // Check a token and advance + // + + template + const bool check(string& line, string::size_type* currentPos, + const bool noThrow = false) + { + try + { + TYPE term; + term.go(*this, line, currentPos); + +#if DEBUG_RESPONSE + std::cout << DEBUG_RESPONSE_level << "SUCCESS! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; + DEBUG_RESPONSE_components.pop_back(); +#endif + } + catch (...) + { +#if DEBUG_RESPONSE + std::cout << DEBUG_RESPONSE_level << "FAILED! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; + DEBUG_RESPONSE_components.pop_back(); +#endif + + if (!noThrow) throw; + return false; + } + + return true; + } + + template + const bool checkWithArg(string& line, string::size_type* currentPos, + const ARG_TYPE arg, const bool noThrow = false) + { + try + { + TYPE term(arg); + term.go(*this, line, currentPos); + +#if DEBUG_RESPONSE + std::cout << DEBUG_RESPONSE_level << "SUCCESS! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; + DEBUG_RESPONSE_components.pop_back(); +#endif + } + catch (...) + { +#if DEBUG_RESPONSE + std::cout << DEBUG_RESPONSE_level << "FAILED! (" << DEBUG_RESPONSE_components.back() << ")" << std::endl; + DEBUG_RESPONSE_components.pop_back(); +#endif + + if (!noThrow) throw; + return false; + } + + return true; + } + + +private: + + IMAPTag* m_tag; + socket* m_socket; + + progressionListener* m_progress; + + literalHandler* m_literalHandler; + + timeoutHandler* m_timeoutHandler; + + + string m_buffer; + int m_pos; + + string m_lastLine; + +public: + + // + // Read one line + // + + const string readLine() + { + string::size_type pos; + + while ((pos = m_buffer.find('\n')) == string::npos) + { + read(); + } + + string line; + line.resize(pos + 1); + std::copy(m_buffer.begin(), m_buffer.begin() + pos + 1, line.begin()); + + m_buffer.erase(m_buffer.begin(), m_buffer.begin() + pos + 1); + + m_lastLine = line; + +#if DEBUG_RESPONSE + std::cout << std::endl << "Read line:" << std::endl << line << std::endl; +#endif + + return (line); + } + + + // + // Read available data from socket stream + // + + void read() + { + string receiveBuffer; + + while (receiveBuffer.empty()) + { + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) + { + if (!m_timeoutHandler->handleTimeOut()) + throw exceptions::operation_timed_out(); + } + + // We have received data: reset the time-out counter + m_socket->receive(receiveBuffer); + + if (receiveBuffer.empty()) // buffer is empty + { + platformDependant::getHandler()->wait(); + continue; + } + + // We have received data ... + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + } + + m_buffer += receiveBuffer; + } + + + void readLiteral(literalHandler::target& buffer, string::size_type count) + { + string::size_type len = 0; + string receiveBuffer; + + if (m_progress) + m_progress->start(count); + + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + if (!m_buffer.empty()) + { + if (m_buffer.length() > count) + { + buffer.putData(string(m_buffer.begin(), m_buffer.begin() + count)); + m_buffer.erase(m_buffer.begin(), m_buffer.begin() + count); + len = count; + } + else + { + len += m_buffer.length(); + buffer.putData(m_buffer); + m_buffer.clear(); + } + } + + while (len < count) + { + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) + { + if (!m_timeoutHandler->handleTimeOut()) + throw exceptions::operation_timed_out(); + } + + // Receive data from the socket + m_socket->receive(receiveBuffer); + + if (receiveBuffer.empty()) // buffer is empty + { + platformDependant::getHandler()->wait(); + continue; + } + + // We have received data: reset the time-out counter + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + if (len + receiveBuffer.length() > count) + { + const string::size_type remaining = count - len; + + // Get the needed amount of data + buffer.putData(string(receiveBuffer.begin(), receiveBuffer.begin() + remaining)); + + // Put the remaining data into the internal response buffer + receiveBuffer.erase(receiveBuffer.begin(), receiveBuffer.begin() + remaining); + m_buffer += receiveBuffer; + + len = count; + } + else + { + buffer.putData(receiveBuffer); + len += receiveBuffer.length(); + } + + // Notify progression + if (m_progress) + m_progress->progress(len, count); + } + + if (m_progress) + m_progress->stop(count); + } +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPPARSER_HPP_INCLUDED diff --git a/src/messaging/IMAPStore.cpp b/src/messaging/IMAPStore.cpp new file mode 100644 index 00000000..7d2bc823 --- /dev/null +++ b/src/messaging/IMAPStore.cpp @@ -0,0 +1,257 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "IMAPStore.hpp" +#include "IMAPFolder.hpp" +#include "IMAPConnection.hpp" + +#include "../exception.hpp" +#include "../platformDependant.hpp" + +#include + + +namespace vmime { +namespace messaging { + + +// +// IMAPauthenticator: private class used internally +// +// Used to request user credentials only in the first authentication +// and reuse this information the next times +// + +class IMAPauthenticator : public authenticator +{ +public: + + IMAPauthenticator(authenticator* auth) + : m_auth(auth), m_infos(NULL) + { + } + + ~IMAPauthenticator() + { + delete (m_infos); + } + + const authenticationInfos requestAuthInfos() const + { + if (m_infos == NULL) + m_infos = new authenticationInfos(m_auth->requestAuthInfos()); + + return (*m_infos); + } + +private: + + authenticator* m_auth; + mutable authenticationInfos* m_infos; +}; + + + +// +// IMAPStore +// + +IMAPStore::IMAPStore(class session& sess, class authenticator* auth) + : store(sess, infosInstance(), auth), + m_connection(NULL), m_oneTimeAuth(NULL) +{ +} + + +IMAPStore::~IMAPStore() +{ + if (isConnected()) + disconnect(); +} + + +authenticator* IMAPStore::oneTimeAuthenticator() +{ + return (m_oneTimeAuth); +} + + +const string IMAPStore::protocolName() const +{ + return "imap"; +} + + +folder* IMAPStore::getRootFolder() +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new IMAPFolder(folder::path(), this); +} + + +folder* IMAPStore::getDefaultFolder() +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new IMAPFolder(folder::path::component("INBOX"), this); +} + + +folder* IMAPStore::getFolder(const folder::path& path) +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new IMAPFolder(path, this); +} + + +void IMAPStore::connect() +{ + if (isConnected()) + throw exceptions::already_connected(); + + m_oneTimeAuth = new IMAPauthenticator(&authenticator()); + + m_connection = new IMAPConnection(this, m_oneTimeAuth); + + try + { + m_connection->connect(); + } + catch (std::exception&) + { + delete (m_connection); + m_connection = NULL; + throw; + } +} + + +const bool IMAPStore::isConnected() const +{ + return (m_connection && m_connection->isConnected()); +} + + +void IMAPStore::disconnect() +{ + if (!isConnected()) + throw exceptions::not_connected(); + + for (std::list ::iterator it = m_folders.begin() ; + it != m_folders.end() ; ++it) + { + (*it)->onStoreDisconnected(); + } + + m_folders.clear(); + + + m_connection->disconnect(); + + delete (m_oneTimeAuth); + m_oneTimeAuth = NULL; + + delete (m_connection); + m_connection = NULL; +} + + +void IMAPStore::noop() +{ + if (!isConnected()) + throw exceptions::not_connected(); + + m_connection->send(true, "NOOP", true); + + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("NOOP", m_connection->parser()->lastLine()); + } +} + + +IMAPConnection* IMAPStore::connection() +{ + return (m_connection); +} + + +void IMAPStore::registerFolder(IMAPFolder* folder) +{ + m_folders.push_back(folder); +} + + +void IMAPStore::unregisterFolder(IMAPFolder* folder) +{ + std::list ::iterator it = std::find(m_folders.begin(), m_folders.end(), folder); + if (it != m_folders.end()) m_folders.erase(it); +} + + + + +// Service infos + +IMAPStore::_infos IMAPStore::sm_infos; + + +const port_t IMAPStore::_infos::defaultPort() const +{ + return (143); +} + + +const string IMAPStore::_infos::propertyPrefix() const +{ + return "store.imap."; +} + + +const std::vector IMAPStore::_infos::availableProperties() const +{ + std::vector list; + + // IMAP-specific options + //list.push_back("auth.mechanism"); + + // Common properties + list.push_back("auth.username"); + list.push_back("auth.password"); + + list.push_back("server.address"); + list.push_back("server.port"); + list.push_back("server.socket-factory"); + + list.push_back("timeout.factory"); + + return (list); +} + + +} // messaging +} // vmime diff --git a/src/messaging/IMAPStore.hpp b/src/messaging/IMAPStore.hpp new file mode 100644 index 00000000..cab9419f --- /dev/null +++ b/src/messaging/IMAPStore.hpp @@ -0,0 +1,112 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPSTORE_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPSTORE_HPP_INCLUDED + + +#include "store.hpp" +#include "socket.hpp" +#include "folder.hpp" +#include "../config.hpp" + +#include + + +namespace vmime { +namespace messaging { + + +class IMAPParser; +class IMAPTag; +class IMAPConnection; + + +/** IMAP store service. + */ + +class IMAPStore : public store +{ + friend class IMAPFolder; + friend class IMAPMessage; + +public: + + IMAPStore(class session& sess, class authenticator* auth); + ~IMAPStore(); + + const string protocolName() const; + + folder* getDefaultFolder(); + folder* getRootFolder(); + folder* getFolder(const folder::path& path); + + static const serviceInfos& infosInstance() { return (sm_infos); } + const serviceInfos& infos() const { return (sm_infos); } + + void connect(); + const bool isConnected() const; + void disconnect(); + + void noop(); + +private: + + // Connection + IMAPConnection* m_connection; + + // Used to request the authentication informations only the + // first time, and reuse these informations the next time. + class authenticator* m_oneTimeAuth; + + + + class authenticator* oneTimeAuthenticator(); + + + IMAPConnection* connection(); + + + void registerFolder(IMAPFolder* folder); + void unregisterFolder(IMAPFolder* folder); + + std::list m_folders; + + + + // Service infos + class _infos : public serviceInfos + { + public: + + const port_t defaultPort() const; + + const string propertyPrefix() const; + const std::vector availableProperties() const; + }; + + static _infos sm_infos; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPSTORE_HPP_INCLUDED diff --git a/src/messaging/IMAPTag.cpp b/src/messaging/IMAPTag.cpp new file mode 100644 index 00000000..023130bb --- /dev/null +++ b/src/messaging/IMAPTag.cpp @@ -0,0 +1,97 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "IMAPTag.hpp" + + +namespace vmime { +namespace messaging { + + +const int IMAPTag::sm_maxNumber = 52 * 10 * 10 * 10; + + +IMAPTag::IMAPTag(const int number) + : m_number(number) +{ + m_tag.resize(4); +} + + +IMAPTag::IMAPTag(const IMAPTag& tag) + : m_number(tag.m_number) +{ + m_tag.resize(4); +} + + +IMAPTag::IMAPTag() + : m_number(0) +{ + m_tag.resize(4); +} + + +IMAPTag& IMAPTag::operator++() +{ + ++m_number; + + if (m_number >= sm_maxNumber) + m_number = 1; + + generate(); + + return (*this); +} + + +const IMAPTag IMAPTag::operator++(int) +{ + IMAPTag old(*this); + operator++(); + return (old); +} + + +const int IMAPTag::number() const +{ + return (m_number); +} + + +IMAPTag::operator string() const +{ + return (m_tag); +} + + +void IMAPTag::generate() +{ + static const char prefixChars[53] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + m_tag[0] = prefixChars[m_number / 1000]; + m_tag[1] = '0' + (m_number % 1000) / 100; + m_tag[2] = '0' + (m_number % 100) / 10; + m_tag[3] = '0' + (m_number % 10); +} + + +} // messaging +} // vmime diff --git a/src/messaging/IMAPTag.hpp b/src/messaging/IMAPTag.hpp new file mode 100644 index 00000000..c7c48f31 --- /dev/null +++ b/src/messaging/IMAPTag.hpp @@ -0,0 +1,64 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPTAG_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPTAG_HPP_INCLUDED + + +#include "types.hpp" + + +namespace vmime { +namespace messaging { + + +class IMAPTag +{ +private: + + IMAPTag(const int number); + IMAPTag(const IMAPTag& tag); + +public: + + IMAPTag(); + + IMAPTag& operator++(); // ++IMAPTag + const IMAPTag operator++(int); // IMAPTag++ + + const int number() const; + + operator string() const; + +private: + + void generate(); + + static const int sm_maxNumber; + + int m_number; + string m_tag; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPTAG_HPP_INCLUDED diff --git a/src/messaging/IMAPUtils.cpp b/src/messaging/IMAPUtils.cpp new file mode 100644 index 00000000..2413c86d --- /dev/null +++ b/src/messaging/IMAPUtils.cpp @@ -0,0 +1,553 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "IMAPUtils.hpp" +#include "message.hpp" + +#include +#include +#include + + +namespace vmime { +namespace messaging { + + +const string IMAPUtils::quoteString(const string& text) +{ + // + // ATOM_CHAR ::= + // + // atom_specials ::= "(" / ")" / "{" / SPACE / CTL / + // list_wildcards / quoted_specials + // + // list_wildcards ::= "%" / "*" + // + // quoted_specials ::= <"> / "\" + // + // CHAR ::= + // + // CTL ::= + // + + bool needQuoting = text.empty(); + + for (string::const_iterator it = text.begin() ; + !needQuoting && it != text.end() ; ++it) + { + const unsigned char c = *it; + + switch (c) + { + case '(': + case ')': + case '{': + case 0x20: // SPACE + case '%': + case '*': + case '"': + case '\\': + + needQuoting = true; + break; + + default: + + if (c <= 0x1f || c >= 0x7f) + needQuoting = true; + } + } + + if (needQuoting) + { + string quoted; + quoted.reserve((text.length() * 3) / 2 + 2); + + quoted += '"'; + + for (string::const_iterator it = text.begin() ; + !needQuoting && it != text.end() ; ++it) + { + const unsigned char c = *it; + + if (c == '\\' || c == '"') + quoted += '\\'; + + quoted += c; + } + + quoted += '"'; + + return (quoted); + } + else + { + return (text); + } +} + + +const string IMAPUtils::pathToString + (const char hierarchySeparator, const folder::path& path) +{ + string result; + + for (int i = 0 ; i < path.size() ; ++i) + { + if (i > 0) result += hierarchySeparator; + result += toModifiedUTF7(hierarchySeparator, path[i]); + } + + return (result); +} + + +const folder::path IMAPUtils::stringToPath + (const char hierarchySeparator, const string& str) +{ + folder::path result; + string::const_iterator begin = str.begin(); + + for (string::const_iterator it = str.begin() ; it != str.end() ; ++it) + { + if (*it == hierarchySeparator) + { + result /= fromModifiedUTF7(string(begin, it)); + begin = it + 1; + } + } + + if (begin != str.end()) + { + result /= fromModifiedUTF7(string(begin, str.end())); + } + + return (result); +} + + +const string IMAPUtils::toModifiedUTF7 + (const char hierarchySeparator, const folder::path::component& text) +{ + // We will replace the hierarchy separator with an equivalent + // UTF-7 sequence, so we compute it here... + const char base64alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,="; + + const unsigned int hs = (unsigned int)(unsigned char) hierarchySeparator; + + string hsUTF7; + hsUTF7.resize(3); + + hsUTF7[0] = base64alphabet[0]; + hsUTF7[1] = base64alphabet[(hs & 0xF0) >> 4]; + hsUTF7[2] = base64alphabet[(hs & 0x0F) << 2]; + + // Transcode path component to UTF-7 charset. + // WARNING: This may throw "exceptions::charset_conv_error" + const string cvt = text.getConvertedText(charset(charsets::UTF_7)); + + // Transcode to modified UTF-7 (RFC-2060). + string out; + out.reserve((cvt.length() * 3) / 2); + + bool inB64sequence = false; + + for (string::const_iterator it = cvt.begin() ; it != cvt.end() ; ++it) + { + const unsigned char c = *it; + + // Replace hierarchy separator with an equivalent UTF-7 Base64 sequence + if (!inB64sequence && c == hierarchySeparator) + { + out += "&" + hsUTF7 + "-"; + continue; + } + + switch (c) + { + // Beginning of Base64 sequence: replace '+' with '&' + case '+': + { + if (!inB64sequence) + { + inB64sequence = true; + out += '&'; + } + else + { + out += '+'; + } + + break; + } + // End of Base64 sequence + case '-': + { + inB64sequence = false; + out += '-'; + break; + } + // ',' is used instead of '/' in modified Base64 + case '/': + { + out += inB64sequence ? ',' : '/'; + break; + } + // '&' (0x26) is represented by the two-octet sequence "&-" + case '&': + { + if (!inB64sequence) + out += "&-"; + else + out += '&'; + + break; + } + default: + { + out += c; + break; + } + + } + } + + return (out); +} + + +const folder::path::component IMAPUtils::fromModifiedUTF7(const string& text) +{ + // Transcode from modified UTF-7 (RFC-2060). + string out; + out.reserve(text.length()); + + bool inB64sequence = false; + unsigned char prev = 0; + + for (string::const_iterator it = text.begin() ; it != text.end() ; ++it) + { + const unsigned char c = *it; + + switch (c) + { + // Start of Base64 sequence + case '&': + { + if (!inB64sequence) + { + inB64sequence = true; + out += '+'; + } + else + { + out += '&'; + } + + break; + } + // End of Base64 sequence (or "&-" --> "&") + case '-': + { + if (inB64sequence && prev == '&') + out += '&'; + else + out += '-'; + + inB64sequence = false; + break; + } + // ',' is used instead of '/' in modified Base64 + case ',': + { + out += (inB64sequence ? '/' : ','); + break; + } + default: + { + out += c; + break; + } + + } + + prev = c; + } + + // Store it as UTF-8 by default + string cvt; + charset::convert(out, cvt, + charset(charsets::UTF_7), charset(charsets::UTF_8)); + + return (folder::path::component(cvt, charset(charsets::UTF_8))); +} + + +const int IMAPUtils::folderTypeFromFlags(const IMAPParser::mailbox_flag_list* list) +{ + // Get folder type + int type = folder::TYPE_CONTAINS_MESSAGES | folder::TYPE_CONTAINS_FOLDERS; + const std::vector & flags = list->flags(); + + for (std::vector ::const_iterator it = flags.begin() ; + it != flags.end() ; ++it) + { + if ((*it)->type() == IMAPParser::mailbox_flag::NOSELECT) + type &= ~folder::TYPE_CONTAINS_MESSAGES; + } + + if (type & folder::TYPE_CONTAINS_MESSAGES) + type &= ~folder::TYPE_CONTAINS_FOLDERS; + + return (type); +} + + +const int IMAPUtils::folderFlagsFromFlags(const IMAPParser::mailbox_flag_list* list) +{ + // Get folder flags + int folderFlags = folder::FLAG_CHILDREN; + const std::vector & flags = list->flags(); + + for (std::vector ::const_iterator it = flags.begin() ; + it != flags.end() ; ++it) + { + if ((*it)->type() == IMAPParser::mailbox_flag::NOSELECT) + folderFlags |= folder::FLAG_NO_OPEN; + else if ((*it)->type() == IMAPParser::mailbox_flag::NOINFERIORS) + folderFlags &= ~folder::FLAG_CHILDREN; + } + + return (folderFlags); +} + + +const int IMAPUtils::messageFlagsFromFlags(const IMAPParser::flag_list* list) +{ + const std::vector & flagList = list->flags(); + int flags = 0; + + for (std::vector ::const_iterator + it = flagList.begin() ; it != flagList.end() ; ++it) + { + switch ((*it)->type()) + { + case IMAPParser::flag::ANSWERED: + flags |= message::FLAG_REPLIED; + break; + case IMAPParser::flag::FLAGGED: + flags |= message::FLAG_MARKED; + break; + case IMAPParser::flag::DELETED: + flags |= message::FLAG_DELETED; + break; + case IMAPParser::flag::SEEN: + flags |= message::FLAG_SEEN; + break; + + default: + //case IMAPParser::flag::UNKNOWN: + //case IMAPParser::flag::DRAFT: + break; + } + } + + return (flags); +} + + +const string IMAPUtils::messageFlagList(const int flags) +{ + std::vector flagList; + + if (flags & message::FLAG_REPLIED) flagList.push_back("\\Answered"); + if (flags & message::FLAG_MARKED) flagList.push_back("\\Flagged"); + if (flags & message::FLAG_DELETED) flagList.push_back("\\Deleted"); + if (flags & message::FLAG_SEEN) flagList.push_back("\\Seen"); + + if (!flagList.empty()) + { + std::ostringstream res; + res << "("; + + if (flagList.size() >= 2) + { + std::copy(flagList.begin(), flagList.end() - 1, + std::ostream_iterator (res, " ")); + } + + res << *(flagList.end() - 1) << ")"; + + return (res.str()); + } + + return ""; +} + + +// This function builds a "IMAP set" given a list. Try to group consecutive +// message numbers to reduce the list. +// +// Example: +// IN = "1,2,3,4,5,7,8,13,15,16,17" +// OUT = "1:5,7:8,13,15:*" for a mailbox with a total of 17 messages (max = 17) + +const string IMAPUtils::listToSet(const std::vector & list, const int max, + const bool alreadySorted) +{ + // Sort a copy of the list (if not already sorted) + std::vector temp; + + if (!alreadySorted) + { + temp.resize(list.size()); + std::copy(list.begin(), list.end(), temp.begin()); + + std::sort(temp.begin(), temp.end()); + } + + const std::vector & theList = (alreadySorted ? list : temp); + + // Build the set + std::ostringstream res; + int previous = -1, setBegin = -1; + + for (std::vector ::const_iterator it = theList.begin() ; + it != theList.end() ; ++it) + { + const int current = *it; + + if (previous == -1) + { + res << current; + + previous = current; + setBegin = current; + } + else + { + if (current == previous + 1) + { + previous = current; + } + else + { + if (setBegin != previous) + { + res << ":" << previous << "," << current; + + previous = current; + setBegin = current; + } + else + { + if (setBegin != current) // skip duplicates + res << "," << current; + + previous = current; + setBegin = current; + } + } + } + } + + if (previous != setBegin) + { + if (previous == max) + res << ":*"; + else + res << ":" << previous; + } + + return (res.str()); +} + + +const string IMAPUtils::dateTime(const vmime::datetime& date) +{ + std::ostringstream res; + + // date_time ::= <"> date_day_fixed "-" date_month "-" date_year + // SPACE time SPACE zone <"> + // + // time ::= 2digit ":" 2digit ":" 2digit + // ;; Hours minutes seconds + // zone ::= ("+" / "-") 4digit + // ;; Signed four-digit value of hhmm representing + // ;; hours and minutes west of Greenwich + res << '"'; + + // Date + if (date.day() < 10) res << ' '; + res << date.day(); + + res << '-'; + + static const char* monthNames[12] = + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + res << monthNames[std::min(std::max(date.month() - 1, 0), 11)]; + + res << '-'; + + if (date.year() < 10) res << '0'; + if (date.year() < 100) res << '0'; + if (date.year() < 1000) res << '0'; + res << date.year(); + + res << ' '; + + // Time + if (date.hour() < 10) res << '0'; + res << date.hour() << ':'; + + if (date.minute() < 10) res << '0'; + res << date.minute() << ':'; + + if (date.second() < 10) res << '0'; + res << date.second(); + + res << ' '; + + // Zone + const int zs = (date.zone() < 0 ? -1 : 1); + const int zh = (date.zone() * zs) / 60; + const int zm = (date.zone() * zs) % 60; + + res << (zs < 0 ? '-' : '+'); + + if (zh < 10) res << '0'; + res << zh; + + if (zm < 10) res << '0'; + res << zm; + + res << '"'; + + + return (res.str()); +} + + +} // messaging +} // vmime diff --git a/src/messaging/IMAPUtils.hpp b/src/messaging/IMAPUtils.hpp new file mode 100644 index 00000000..9d523eb7 --- /dev/null +++ b/src/messaging/IMAPUtils.hpp @@ -0,0 +1,65 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_IMAPUTILS_HPP_INCLUDED +#define VMIME_MESSAGING_IMAPUTILS_HPP_INCLUDED + + +#include "folder.hpp" +#include "../types.hpp" +#include "IMAPParser.hpp" +#include "../dateTime.hpp" + +#include + + +namespace vmime { +namespace messaging { + + +class IMAPUtils +{ +public: + + static const string pathToString(const char hierarchySeparator, const folder::path& path); + static const folder::path stringToPath(const char hierarchySeparator, const string& str); + + static const string toModifiedUTF7(const char hierarchySeparator, const folder::path::component& text); + static const folder::path::component fromModifiedUTF7(const string& text); + + static const string quoteString(const string& text); + + static const int folderTypeFromFlags(const IMAPParser::mailbox_flag_list* list); + static const int folderFlagsFromFlags(const IMAPParser::mailbox_flag_list* list); + + static const int messageFlagsFromFlags(const IMAPParser::flag_list* list); + + static const string messageFlagList(const int flags); + + static const string listToSet(const std::vector & list, const int max = -1, const bool alreadySorted = false); + + static const string dateTime(const vmime::datetime& date); +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_IMAPUTILS_HPP_INCLUDED diff --git a/src/messaging/POP3Folder.cpp b/src/messaging/POP3Folder.cpp new file mode 100644 index 00000000..663d03b5 --- /dev/null +++ b/src/messaging/POP3Folder.cpp @@ -0,0 +1,661 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "POP3Folder.hpp" + +#include "POP3Store.hpp" +#include "POP3Message.hpp" + +#include "../exception.hpp" + + +namespace vmime { +namespace messaging { + + +POP3Folder::POP3Folder(const folder::path& path, POP3Store* store) + : m_store(store), m_path(path), m_name(path.last()), m_mode(-1), m_open(false) +{ + m_store->registerFolder(this); +} + + +POP3Folder::~POP3Folder() +{ + if (m_store) + { + if (m_open) + close(false); + + m_store->unregisterFolder(this); + } + else if (m_open) + { + onClose(); + } +} + + +const int POP3Folder::mode() const +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + return (m_mode); +} + + +const int POP3Folder::type() +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + if (m_path.empty()) + return (TYPE_CONTAINS_FOLDERS); + else if (m_path.size() == 1 && m_path[0].buffer() == "INBOX") + return (TYPE_CONTAINS_MESSAGES); + else + throw exceptions::folder_not_found(); +} + + +const int POP3Folder::flags() +{ + return (0); +} + + +const folder::path::component POP3Folder::name() const +{ + return (m_name); +} + + +const folder::path POP3Folder::fullPath() const +{ + return (m_path); +} + + +void POP3Folder::open(const int mode, bool failIfModeIsNotAvailable) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + if (m_path.empty()) + { + if (mode != MODE_READ_ONLY && failIfModeIsNotAvailable) + throw exceptions::operation_not_supported(); + + m_open = true; + m_mode = mode; + + m_messageCount = 0; + } + else if (m_path.size() == 1 && m_path[0].buffer() == "INBOX") + { + m_store->sendRequest("STAT"); + + string response; + m_store->readResponse(response, false); + + if (!m_store->isSuccessResponse(response)) + throw exceptions::command_error("STAT", response); + + m_store->stripResponseCode(response, response); + + std::istringstream iss(response); + iss >> m_messageCount; + + if (iss.fail()) + throw exceptions::invalid_response("STAT", response); + + m_open = true; + m_mode = mode; + } + else + { + throw exceptions::folder_not_found(); + } +} + +void POP3Folder::close(const bool expunge) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + if (!expunge) + { + m_store->sendRequest("RSET"); + + string response; + m_store->readResponse(response, false); + } + + m_open = false; + m_mode = -1; + + onClose(); +} + + +void POP3Folder::onClose() +{ + for (MessageMap::iterator it = m_messages.begin() ; it != m_messages.end() ; ++it) + (*it).first->onFolderClosed(); + + m_messages.clear(); +} + + +void POP3Folder::create(const int /* type */) +{ + throw exceptions::operation_not_supported(); +} + + +const bool POP3Folder::exists() +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + return (m_path.empty() || (m_path.size() == 1 && m_path[0].buffer() == "INBOX")); +} + + +const bool POP3Folder::isOpen() const +{ + return (m_open); +} + + +message* POP3Folder::getMessage(const int num) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (num < 1 || num > m_messageCount) + throw exceptions::message_not_found(); + + return new POP3Message(this, num); +} + + +std::vector POP3Folder::getMessages(const int from, const int to) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + else if (to < from || from < 1 || to < 1 || from > m_messageCount || to > m_messageCount) + throw exceptions::message_not_found(); + + std::vector v; + + for (int i = from ; i <= to ; ++i) + v.push_back(new POP3Message(this, i)); + + return (v); +} + + +std::vector POP3Folder::getMessages(const std::vector & nums) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + std::vector v; + + try + { + for (std::vector ::const_iterator it = nums.begin() ; it != nums.end() ; ++it) + { + if (*it < 1|| *it > m_messageCount) + throw exceptions::message_not_found(); + + v.push_back(new POP3Message(this, *it)); + } + } + catch (std::exception& e) + { + for (std::vector ::iterator it = v.begin() ; it != v.end() ; ++it) + delete (*it); + + throw; + } + + return (v); +} + + +const int POP3Folder::getMessageCount() +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + return (m_messageCount); +} + + +folder* POP3Folder::getFolder(const folder::path::component& name) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + return new POP3Folder(m_path / name, m_store); +} + + +std::vector POP3Folder::getFolders(const bool /* recursive */) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + if (m_path.empty()) + { + std::vector v; + v.push_back(new POP3Folder(folder::path::component("INBOX"), m_store)); + return (v); + } + else + { + std::vector v; + return (v); + } +} + + +void POP3Folder::fetchMessages(std::vector & msg, const int options, + progressionListener* progress) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + const int total = msg.size(); + int current = 0; + + if (progress) + progress->start(total); + + for (std::vector ::iterator it = msg.begin() ; + it != msg.end() ; ++it) + { + dynamic_cast (*it)->fetch(this, options); + + if (progress) + progress->progress(++current, total); + } + + if (options & FETCH_UID) + { + // Send the "UIDL" command + std::ostringstream command; + command << "UIDL"; + + m_store->sendRequest(command.str()); + + // Get the response + string response; + m_store->readResponse(response, true, NULL); + + if (m_store->isSuccessResponse(response)) + { + m_store->stripFirstLine(response, response, NULL); + + // C: UIDL + // S: +OK + // S: 1 whqtswO00WBw418f9t5JxYwZ + // S: 2 QhdPYR:00WBw1Ph7x7 + // S: . + + std::istringstream iss(response); + std::map ids; + + string line; + + while (std::getline(iss, line)) + { + string::iterator it = line.begin(); + + while (it != line.end() && (*it == ' ' || *it == '\t')) + ++it; + + if (it != line.end()) + { + int number = 0; + + while (it != line.end() && (*it >= '0' && *it <= '9')) + { + number = (number * 10) + (*it - '0'); + ++it; + } + + while (it != line.end() && !(*it == ' ' || *it == '\t')) ++it; + while (it != line.end() && (*it == ' ' || *it == '\t')) ++it; + + if (it != line.end()) + { + ids.insert(std::map ::value_type + (number, string(it, line.end()))); + } + } + } + + for (std::vector ::iterator it = msg.begin() ; + it != msg.end() ; ++it) + { + POP3Message* m = dynamic_cast (*it); + + std::map ::const_iterator id = + ids.find(m->m_num); + + if (id != ids.end()) + m->m_uid = (*id).second; + } + } + } + + if (progress) + progress->stop(total); +} + + +void POP3Folder::fetchMessage(message* msg, const int options) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + dynamic_cast (msg)->fetch(this, options); + + if (options & FETCH_UID) + { + // Send the "UIDL" command + std::ostringstream command; + command << "UIDL"; + + m_store->sendRequest(command.str()); + + // Get the response + string response; + m_store->readResponse(response, false, NULL); + + if (m_store->isSuccessResponse(response)) + { + m_store->stripResponseCode(response, response); + + // C: UIDL 2 + // S: +OK 2 QhdPYR:00WBw1Ph7x7 + string::iterator it = response.begin(); + + while (it != response.end() && (*it == ' ' || *it == '\t')) ++it; + while (it != response.end() && !(*it == ' ' || *it == '\t')) ++it; + while (it != response.end() && (*it == ' ' || *it == '\t')) ++it; + + if (it != response.end()) + { + dynamic_cast (msg)->m_uid = + string(it, response.end()); + } + } + } +} + + +const int POP3Folder::getFetchCapabilities() const +{ + return (FETCH_ENVELOPE | FETCH_CONTENT_INFO | + FETCH_SIZE | FETCH_FULL_HEADER | FETCH_UID); +} + + +folder* POP3Folder::getParent() +{ + return (m_path.empty() ? NULL : new POP3Folder(m_path.parent(), m_store)); +} + + +const class store& POP3Folder::store() const +{ + return (*m_store); +} + + +class store& POP3Folder::store() +{ + return (*m_store); +} + + +void POP3Folder::registerMessage(POP3Message* msg) +{ + m_messages.insert(MessageMap::value_type(msg, msg->number())); +} + + +void POP3Folder::unregisterMessage(POP3Message* msg) +{ + m_messages.erase(msg); +} + + +void POP3Folder::onStoreDisconnected() +{ + m_store = NULL; +} + + +void POP3Folder::deleteMessage(const int num) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + std::ostringstream command; + command << "DELE " << num; + + m_store->sendRequest(command.str()); + + string response; + m_store->readResponse(response, false); + + if (!m_store->isSuccessResponse(response)) + throw exceptions::command_error("DELE", response); +} + + +void POP3Folder::deleteMessages(const int from, const int to) +{ + if (from < 1 || (to < from && to != -1)) + throw exceptions::invalid_argument(); + + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + for (int i = from ; i < to ; ++i) + { + std::ostringstream command; + command << "DELE " << i; + + m_store->sendRequest(command.str()); + + string response; + m_store->readResponse(response, false); + + if (!m_store->isSuccessResponse(response)) + throw exceptions::command_error("DELE", response); + } +} + + +void POP3Folder::deleteMessages(const std::vector & nums) +{ + if (nums.empty()) + throw exceptions::invalid_argument(); + + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + for (std::vector ::const_iterator + it = nums.begin() ; it != nums.end() ; ++it) + { + std::ostringstream command; + command << "DELE " << (*it); + + m_store->sendRequest(command.str()); + + string response; + m_store->readResponse(response, false); + + if (!m_store->isSuccessResponse(response)) + throw exceptions::command_error("DELE", response); + } +} + + +void POP3Folder::setMessageFlags(const int /* from */, const int /* to */, + const int /* flags */, const int /* mode */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::setMessageFlags(const std::vector & /* nums */, + const int /* flags */, const int /* mode */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::rename(const folder::path& /* newPath */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::addMessage(vmime::message* /* msg */, const int /* flags */, + vmime::datetime* /* date */, progressionListener* /* progress */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::addMessage(utility::inputStream& /* is */, const int /* size */, const int /* flags */, + vmime::datetime* /* date */, progressionListener* /* progress */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::copyMessage(const folder::path& /* dest */, const int /* num */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::copyMessages(const folder::path& /* dest */, const int /* from */, const int /* to */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::copyMessages(const folder::path& /* dest */, const std::vector & /* nums */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Folder::status(int& count, int& unseen) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + m_store->sendRequest("STAT"); + + string response; + m_store->readResponse(response, false); + + if (!m_store->isSuccessResponse(response)) + throw exceptions::command_error("STAT", response); + + m_store->stripResponseCode(response, response); + + std::istringstream iss(response); + iss >> count; + + unseen = count; + + // Update local message count + if (m_messageCount != count) + { + const int oldCount = m_messageCount; + + m_messageCount = count; + + if (count > oldCount) + { + std::vector nums; + nums.reserve(count - oldCount); + + for (int i = oldCount + 1, j = 0 ; i <= count ; ++i, ++j) + nums[j] = i; + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + for (std::list ::iterator it = m_store->m_folders.begin() ; + it != m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == m_path) + { + (*it)->m_messageCount = count; + (*it)->notifyMessageCount(event); + } + } + } + } +} + + +void POP3Folder::expunge() +{ + // Not supported by POP3 protocol (deleted messages are automatically + // expunged at the end of the session...). +} + + +} // messaging +} // vmime diff --git a/src/messaging/POP3Folder.hpp b/src/messaging/POP3Folder.hpp new file mode 100644 index 00000000..cfb83bef --- /dev/null +++ b/src/messaging/POP3Folder.hpp @@ -0,0 +1,142 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_POP3FOLDER_HPP_INCLUDED +#define VMIME_MESSAGING_POP3FOLDER_HPP_INCLUDED + + +#include +#include + +#include "../types.hpp" +#include "folder.hpp" +#include "../config.hpp" + + +namespace vmime { +namespace messaging { + + +class POP3Store; +class POP3Message; + + +/** POP3 folder implementation. + */ + +class POP3Folder : public folder +{ +protected: + + friend class POP3Store; + friend class POP3Message; + + POP3Folder(const folder::path& path, POP3Store* store); + POP3Folder(const POP3Folder&) : folder() { } + + ~POP3Folder(); + +public: + + const int mode() const; + + const int type(); + + const int flags(); + + const folder::path::component name() const; + const folder::path fullPath() const; + + void open(const int mode, bool failIfModeIsNotAvailable = false); + void close(const bool expunge); + void create(const int type); + + const bool exists(); + + const bool isOpen() const; + + message* getMessage(const int num); + std::vector getMessages(const int from = 1, const int to = -1); + std::vector getMessages(const std::vector & nums); + const int getMessageCount(); + + folder* getFolder(const folder::path::component& name); + std::vector getFolders(const bool recursive = false); + + void rename(const folder::path& newPath); + + void deleteMessage(const int num); + void deleteMessages(const int from = 1, const int to = -1); + void deleteMessages(const std::vector & nums); + + void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET); + void setMessageFlags(const std::vector & nums, const int flags, const int mode = message::FLAG_MODE_SET); + + void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + + void copyMessage(const folder::path& dest, const int num); + void copyMessages(const folder::path& dest, const int from = 1, const int to = -1); + void copyMessages(const folder::path& dest, const std::vector & nums); + + void status(int& count, int& unseen); + + void expunge(); + + folder* getParent(); + + const class store& store() const; + class store& store(); + + + void fetchMessages(std::vector & msg, const int options, progressionListener* progress = NULL); + void fetchMessage(message* msg, const int options); + + const int getFetchCapabilities() const; + +private: + + void registerMessage(POP3Message* msg); + void unregisterMessage(POP3Message* msg); + + void onStoreDisconnected(); + + void onClose(); + + + POP3Store* m_store; + + folder::path m_path; + folder::path::component m_name; + + int m_mode; + bool m_open; + + int m_messageCount; + + typedef std::map MessageMap; + MessageMap m_messages; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_POP3FOLDER_HPP_INCLUDED diff --git a/src/messaging/POP3Message.cpp b/src/messaging/POP3Message.cpp new file mode 100644 index 00000000..66b3f466 --- /dev/null +++ b/src/messaging/POP3Message.cpp @@ -0,0 +1,220 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "POP3Message.hpp" +#include "POP3Folder.hpp" +#include "POP3Store.hpp" + +#include + + +namespace vmime { +namespace messaging { + + + +class POP3header : public header +{ +public: + + POP3header(const string& str) + { + parse(str); + } +}; + + + +POP3Message::POP3Message(POP3Folder* folder, const int num) + : m_folder(folder), m_num(num), m_header(NULL) +{ + m_folder->registerMessage(this); +} + + +POP3Message::~POP3Message() +{ + if (m_folder) + m_folder->unregisterMessage(this); + + delete dynamic_cast (m_header); +} + + +void POP3Message::onFolderClosed() +{ + m_folder = NULL; +} + + +const int POP3Message::number() const +{ + return (m_num); +} + + +const message::uid POP3Message::uniqueId() const +{ + return (m_uid); +} + + +const int POP3Message::size() const +{ + if (!m_folder) + throw exceptions::illegal_state("Folder closed"); + + POP3Folder::MessageMap::const_iterator it = + m_folder->m_messages.find(const_cast (this)); + + return ((it != m_folder->m_messages.end()) ? (*it).second : 0); +} + + +const bool POP3Message::isExpunged() const +{ + return (false); +} + + +const int POP3Message::flags() const +{ + return (FLAG_RECENT); +} + + +const class structure& POP3Message::structure() const +{ + throw exceptions::operation_not_supported(); +} + + +class structure& POP3Message::structure() +{ + throw exceptions::operation_not_supported(); +} + + +const class header& POP3Message::header() const +{ + if (m_header == NULL) + throw exceptions::unfetched_object(); + + return (*m_header); +} + + +void POP3Message::extract(utility::outputStream& os, progressionListener* progress, + const int start, const int length) const +{ + if (!m_folder) + throw exceptions::illegal_state("Folder closed"); + else if (!m_folder->m_store) + throw exceptions::illegal_state("Store disconnected"); + + if (start != 0 && length != -1) + throw exceptions::partial_fetch_not_supported(); + + // Emit the "RETR" command + std::ostringstream oss; + oss << "RETR " << m_num; + + const_cast (m_folder)->m_store->sendRequest(oss.str()); + + try + { + POP3Folder::MessageMap::const_iterator it = + m_folder->m_messages.find(const_cast (this)); + + const int totalSize = (it != m_folder->m_messages.end()) + ? (*it).second : 0; + + const_cast (m_folder)->m_store-> + readResponse(os, progress, totalSize); + } + catch (exceptions::command_error& e) + { + throw exceptions::command_error("RETR", e.response()); + } +} + + +void POP3Message::extractPart + (const part& /* p */, utility::outputStream& /* os */, progressionListener* /* progress */, + const int /* start */, const int /* length */) const +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Message::fetchPartHeader(part& /* p */) +{ + throw exceptions::operation_not_supported(); +} + + +void POP3Message::fetch(POP3Folder* folder, const int options) +{ + if (m_folder != folder) + throw exceptions::folder_not_found(); + + // FETCH_STRUCTURE and FETCH_FLAGS are not supported by POP3. + if (options & (folder::FETCH_STRUCTURE | folder::FETCH_FLAGS)) + throw exceptions::operation_not_supported(); + + // Check for the real need to fetch the full header + if (!((options & folder::FETCH_ENVELOPE) || + (options & folder::FETCH_CONTENT_INFO) || + (options & folder::FETCH_FULL_HEADER))) + { + return; + } + + // No need to differenciate between FETCH_ENVELOPE, + // FETCH_CONTENT_INFO, ... since POP3 only permits to + // retrieve the whole header and not fields in particular. + + // Emit the "TOP" command + std::ostringstream oss; + oss << "TOP " << m_num << " 0"; + + m_folder->m_store->sendRequest(oss.str()); + + try + { + string buffer; + m_folder->m_store->readResponse(buffer, true); + + m_header = new POP3header(buffer); + } + catch (exceptions::command_error& e) + { + throw exceptions::command_error("TOP", e.response()); + } +} + + +void POP3Message::setFlags(const int /* flags */, const int /* mode */) +{ + throw exceptions::operation_not_supported(); +} + + +} // messaging +} // vmime diff --git a/src/messaging/POP3Message.hpp b/src/messaging/POP3Message.hpp new file mode 100644 index 00000000..193624b8 --- /dev/null +++ b/src/messaging/POP3Message.hpp @@ -0,0 +1,88 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_POP3MESSAGE_HPP_INCLUDED +#define VMIME_MESSAGING_POP3MESSAGE_HPP_INCLUDED + + +#include "message.hpp" +#include "folder.hpp" +#include "../config.hpp" + + +namespace vmime { +namespace messaging { + + +/** POP3 message implementation. + */ + +class POP3Message : public message +{ +protected: + + friend class POP3Folder; + + POP3Message(POP3Folder* folder, const int num); + POP3Message(const POP3Message&) : message() { } + + ~POP3Message(); + +public: + + const int number() const; + + const uid uniqueId() const; + + const int size() const; + + const bool isExpunged() const; + + const class structure& structure() const; + class structure& structure(); + + const class header& header() const; + + const int flags() const; + void setFlags(const int flags, const int mode = FLAG_MODE_SET); + + void extract(utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + void extractPart(const part& p, utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + + void fetchPartHeader(part& p); + +private: + + void fetch(POP3Folder* folder, const int options); + + void onFolderClosed(); + + POP3Folder* m_folder; + int m_num; + uid m_uid; + + class header* m_header; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_POP3MESSAGE_HPP_INCLUDED diff --git a/src/messaging/POP3Store.cpp b/src/messaging/POP3Store.cpp new file mode 100644 index 00000000..c105bc4b --- /dev/null +++ b/src/messaging/POP3Store.cpp @@ -0,0 +1,603 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "POP3Store.hpp" + +#include "../exception.hpp" +#include "../platformDependant.hpp" +#include "../messageId.hpp" +#include "../utility/md5.hpp" + +#include "POP3Folder.hpp" + +#include + + +namespace vmime { +namespace messaging { + + +POP3Store::POP3Store(class session& sess, class authenticator* auth) + : store(sess, infosInstance(), auth), m_socket(NULL), + m_authentified(false), m_timeoutHandler(NULL) +{ +} + + +POP3Store::~POP3Store() +{ + if (isConnected()) + disconnect(); + else if (m_socket) + internalDisconnect(); +} + + +const string POP3Store::protocolName() const +{ + return "pop3"; +} + + +folder* POP3Store::getDefaultFolder() +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new POP3Folder(folder::path(folder::path::component("INBOX")), this); +} + + +folder* POP3Store::getRootFolder() +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new POP3Folder(folder::path(), this); +} + + +folder* POP3Store::getFolder(const folder::path& path) +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new POP3Folder(path, this); +} + + +void POP3Store::connect() +{ + if (isConnected()) + throw exceptions::already_connected(); + + const string address = session().properties()[sm_infos.propertyPrefix() + "server.address"]; + const port_t port = session().properties().get(sm_infos.propertyPrefix() + "server.port", sm_infos.defaultPort()); + + // Create the time-out handler + if (session().properties().exists + (sm_infos.propertyPrefix() + "timeout.factory")) + { + timeoutHandlerFactory* tof = platformDependant::getHandler()-> + getTimeoutHandlerFactory(session().properties() + [sm_infos.propertyPrefix() + "timeout.factory"]); + + m_timeoutHandler = tof->create(); + } + + // Create and connect the socket + socketFactory* sf = platformDependant::getHandler()->getSocketFactory + (session().properties().get(sm_infos.propertyPrefix() + "server.socket-factory", string("default"))); + + m_socket = sf->create(); + m_socket->connect(address, port); + + // Connection + // + // eg: C: + // --- S: +OK MailSite POP3 Server 5.3.4.0 Ready <36938848.1056800841.634@somewhere.com> + + string response; + readResponse(response, false); + + if (isSuccessResponse(response)) + { + bool authentified = false; + + const authenticationInfos auth = authenticator().requestAuthInfos(); + + // Secured authentication with APOP (if requested and if available) + // + // eg: C: APOP vincent + // --- S: +OK vincent is a valid mailbox + messageId mid(response); + + if (session().properties().get(sm_infos.propertyPrefix() + "options.apop", false)) + { + if (mid.left().length() && mid.right().length()) + { + // is the result of MD5 applied to "password" + sendRequest("APOP " + auth.username() + " " + + utility::md5(mid.generate() + auth.password()).hex()); + readResponse(response, false); + + if (isSuccessResponse(response)) + { + authentified = true; + } + else + { + if (session().properties().get(sm_infos.propertyPrefix() + + "options.apop.fallback", false) == false) + { + internalDisconnect(); + throw exceptions::authentication_error(response); + } + } + } + else + { + // APOP not supported + if (session().properties().get(sm_infos.propertyPrefix() + + "options.apop.fallback", false) == false) + { + // Can't fallback on basic authentification + internalDisconnect(); + throw exceptions::unsupported_option(); + } + } + } + + if (!authentified) + { + // Basic authentication + // + // eg: C: USER vincent + // --- S: +OK vincent is a valid mailbox + // + // C: PASS couic + // S: +OK vincent's maildrop has 2 messages (320 octets) + + sendRequest("USER " + auth.username()); + readResponse(response, false); + + if (isSuccessResponse(response)) + { + sendRequest("PASS " + auth.password()); + readResponse(response, false); + + if (!isSuccessResponse(response)) + { + internalDisconnect(); + throw exceptions::authentication_error(response); + } + } + else + { + internalDisconnect(); + throw exceptions::authentication_error(response); + } + } + } + else + { + internalDisconnect(); + throw exceptions::connection_greeting_error(response); + } + + m_authentified = true; +} + + +const bool POP3Store::isConnected() const +{ + return (m_socket && m_socket->isConnected() && m_authentified); +} + + +void POP3Store::disconnect() +{ + if (!isConnected()) + throw exceptions::not_connected(); + + internalDisconnect(); +} + + +void POP3Store::internalDisconnect() +{ + for (std::list ::iterator it = m_folders.begin() ; + it != m_folders.end() ; ++it) + { + (*it)->onStoreDisconnected(); + } + + m_folders.clear(); + + + sendRequest("QUIT"); + + m_socket->disconnect(); + + delete (m_socket); + m_socket = NULL; + + delete (m_timeoutHandler); + m_timeoutHandler = NULL; + + m_authentified = false; +} + + +void POP3Store::noop() +{ + m_socket->send("NOOP"); + + string response; + readResponse(response, false); + + if (!isSuccessResponse(response)) + throw exceptions::command_error("NOOP", response); +} + + +const bool POP3Store::isSuccessResponse(const string& buffer) +{ + static const string OK("+OK"); + + return (buffer.length() >= 3 && + std::equal(buffer.begin(), buffer.begin() + 3, OK.begin())); +} + + +const bool POP3Store::stripFirstLine(const string& buffer, string& result, string* firstLine) +{ + const string::size_type end = buffer.find('\n'); + + if (end != string::npos) + { + if (firstLine) *firstLine = buffer.substr(0, end); + result = buffer.substr(end + 1); + return (true); + } + else + { + result = buffer; + return (false); + } +} + + +void POP3Store::stripResponseCode(const string& buffer, string& result) +{ + const string::size_type pos = buffer.find_first_of(" \t"); + + if (pos != string::npos) + result = buffer.substr(pos + 1); + else + result = buffer; +} + + +void POP3Store::sendRequest(const string& buffer, const bool end) +{ + m_socket->send(buffer); + if (end) m_socket->send("\r\n"); +} + + +void POP3Store::readResponse(string& buffer, const bool multiLine, + progressionListener* progress) +{ + bool foundTerminator = false; + int current = 0, total = 0; + + if (progress) + progress->start(total); + + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + buffer.clear(); + + string::value_type last1 = '\0', last2 = '\0'; + + for ( ; !foundTerminator ; ) + { +#if 0 // not supported + // Check for possible cancellation + if (progress && progress->cancel()) + throw exceptions::operation_cancelled(); +#endif + + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) + { + if (!m_timeoutHandler->handleTimeOut()) + throw exceptions::operation_timed_out(); + } + + // Receive data from the socket + string receiveBuffer; + m_socket->receive(receiveBuffer); + + if (receiveBuffer.empty()) // buffer is empty + { + platformDependant::getHandler()->wait(); + continue; + } + + // We have received data: reset the time-out counter + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + // Check for transparent characters: '\n..' becomes '\n.' + const string::value_type first = receiveBuffer[0]; + + if (first == '.' && last2 == '\n' && last1 == '.') + { + receiveBuffer.erase(receiveBuffer.begin()); + } + else if (receiveBuffer.length() >= 2 && first == '.' && + receiveBuffer[1] == '.' && last1 == '\n') + { + receiveBuffer.erase(receiveBuffer.begin()); + } + + for (string::size_type trans ; + string::npos != (trans = receiveBuffer.find("\n..")) ; ) + { + receiveBuffer.replace(trans, 3, "\n."); + } + + last1 = receiveBuffer[receiveBuffer.length() - 1]; + last2 = (receiveBuffer.length() >= 2) ? receiveBuffer[receiveBuffer.length() - 2] : 0; + + // Append the data to the response buffer + buffer += receiveBuffer; + current += receiveBuffer.length(); + + // Check for terminator string (and strip it if present) + foundTerminator = checkTerminator(buffer, multiLine); + + // Notify progression + if (progress) + { + total = std::max(total, current); + progress->progress(current, total); + } + + // If there is an error (-ERR) when executing a command that + // requires a multi-line response, the error response will + // include only one line, so we stop waiting for a multi-line + // terminator and check for a "normal" one. + if (multiLine && !foundTerminator && buffer.length() >= 4 && buffer[0] == '-') + { + foundTerminator = checkTerminator(buffer, false); + } + } + + if (progress) + progress->stop(total); +} + + +void POP3Store::readResponse(utility::outputStream& os, progressionListener* progress, + const int predictedSize) +{ + bool foundTerminator = false; + int current = 0, total = predictedSize; + + string temp; + bool codeDone = false; + + if (progress) + progress->start(total); + + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + string::value_type last1 = '\0', last2 = '\0'; + + for ( ; !foundTerminator ; ) + { +#if 0 // not supported + // Check for possible cancellation + if (progress && progress->cancel()) + throw exceptions::operation_cancelled(); +#endif + + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) + { + if (!m_timeoutHandler->handleTimeOut()) + throw exceptions::operation_timed_out(); + } + + // Receive data from the socket + string receiveBuffer; + m_socket->receive(receiveBuffer); + + if (receiveBuffer.empty()) // buffer is empty + { + platformDependant::getHandler()->wait(); + continue; + } + + // We have received data: reset the time-out counter + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + // Check for transparent characters: '\n..' becomes '\n.' + const string::value_type first = receiveBuffer[0]; + + if (first == '.' && last2 == '\n' && last1 == '.') + { + receiveBuffer.erase(receiveBuffer.begin()); + } + else if (receiveBuffer.length() >= 2 && first == '.' && + receiveBuffer[1] == '.' && last1 == '\n') + { + receiveBuffer.erase(receiveBuffer.begin()); + } + + for (string::size_type trans ; + string::npos != (trans = receiveBuffer.find("\n..")) ; ) + { + receiveBuffer.replace(trans, 3, "\n."); + } + + last1 = receiveBuffer[receiveBuffer.length() - 1]; + last2 = (receiveBuffer.length() >= 2) ? receiveBuffer[receiveBuffer.length() - 2] : 0; + + // If we don't have extracted the response code yet + if (!codeDone) + { + temp += receiveBuffer; + + string firstLine; + + if (stripFirstLine(temp, temp, &firstLine) == true) + { + if (!isSuccessResponse(firstLine)) + throw exceptions::command_error("?", firstLine); + + receiveBuffer = temp; + temp.clear(); + + codeDone = true; + } + } + + if (codeDone) + { + // Check for terminator string (and strip it if present) + foundTerminator = checkTerminator(receiveBuffer, true); + + // Inject the data into the output stream + os.write(receiveBuffer.data(), receiveBuffer.length()); + current += receiveBuffer.length(); + + // Notify progression + if (progress) + { + total = std::max(total, current); + progress->progress(current, total); + } + } + } + + if (progress) + progress->stop(total); +} + + +const bool POP3Store::checkTerminator(string& buffer, const bool multiLine) +{ + // Multi-line response + if (multiLine) + { + static const string term1("\r\n.\r\n"); + static const string term2("\n.\n"); + + return (checkOneTerminator(buffer, term1) || + checkOneTerminator(buffer, term2)); + } + // Normal response + else + { + static const string term1("\r\n"); + static const string term2("\n"); + + return (checkOneTerminator(buffer, term1) || + checkOneTerminator(buffer, term2)); + } + + return (false); +} + + +const bool POP3Store::checkOneTerminator(string& buffer, const string& term) +{ + if (buffer.length() >= term.length() && + std::equal(buffer.end() - term.length(), buffer.end(), term.begin())) + { + buffer.erase(buffer.end() - term.length(), buffer.end()); + return (true); + } + + return (false); +} + + +void POP3Store::registerFolder(POP3Folder* folder) +{ + m_folders.push_back(folder); +} + + +void POP3Store::unregisterFolder(POP3Folder* folder) +{ + std::list ::iterator it = std::find(m_folders.begin(), m_folders.end(), folder); + if (it != m_folders.end()) m_folders.erase(it); +} + + + +// Service infos + +POP3Store::_infos POP3Store::sm_infos; + + +const port_t POP3Store::_infos::defaultPort() const +{ + return (110); +} + + +const string POP3Store::_infos::propertyPrefix() const +{ + return "store.pop3."; +} + + +const std::vector POP3Store::_infos::availableProperties() const +{ + std::vector list; + + // POP3-specific options + list.push_back("options.apop"); + list.push_back("options.apop.fallback"); + + // Common properties + list.push_back("auth.username"); + list.push_back("auth.password"); + + list.push_back("server.address"); + list.push_back("server.port"); + list.push_back("server.socket-factory"); + + list.push_back("timeout.factory"); + + return (list); +} + + +} // messaging +} // vmime diff --git a/src/messaging/POP3Store.hpp b/src/messaging/POP3Store.hpp new file mode 100644 index 00000000..b9d6bd87 --- /dev/null +++ b/src/messaging/POP3Store.hpp @@ -0,0 +1,110 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_POP3STORE_HPP_INCLUDED +#define VMIME_MESSAGING_POP3STORE_HPP_INCLUDED + + +#include "store.hpp" +#include "socket.hpp" +#include "../config.hpp" +#include "timeoutHandler.hpp" +#include "../utility/stream.hpp" + + +namespace vmime { +namespace messaging { + + +/** POP3 store service. + */ + +class POP3Store : public store +{ + friend class POP3Folder; + friend class POP3Message; + +public: + + POP3Store(class session& sess, class authenticator* auth); + ~POP3Store(); + + const string protocolName() const; + + folder* getDefaultFolder(); + folder* getRootFolder(); + folder* getFolder(const folder::path& path); + + static const serviceInfos& infosInstance() { return (sm_infos); } + const serviceInfos& infos() const { return (sm_infos); } + + void connect(); + const bool isConnected() const; + void disconnect(); + + void noop(); + +private: + + static const bool isSuccessResponse(const string& buffer); + static const bool stripFirstLine(const string& buffer, string& result, string* firstLine = NULL); + static void stripResponseCode(const string& buffer, string& result); + + void sendRequest(const string& buffer, const bool end = true); + void readResponse(string& buffer, const bool multiLine, progressionListener* progress = NULL); + void readResponse(utility::outputStream& os, progressionListener* progress = NULL, const int predictedSize = 0); + + static const bool checkTerminator(string& buffer, const bool multiLine); + static const bool checkOneTerminator(string& buffer, const string& term); + + void internalDisconnect(); + + + void registerFolder(POP3Folder* folder); + void unregisterFolder(POP3Folder* folder); + + std::list m_folders; + + + socket* m_socket; + bool m_authentified; + + timeoutHandler* m_timeoutHandler; + + + // Service infos + class _infos : public serviceInfos + { + public: + + const port_t defaultPort() const; + + const string propertyPrefix() const; + const std::vector availableProperties() const; + }; + + static _infos sm_infos; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_POP3STORE_HPP_INCLUDED diff --git a/src/messaging/SMTPTransport.cpp b/src/messaging/SMTPTransport.cpp new file mode 100644 index 00000000..309f3e06 --- /dev/null +++ b/src/messaging/SMTPTransport.cpp @@ -0,0 +1,575 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "SMTPTransport.hpp" + +#include "../exception.hpp" +#include "../platformDependant.hpp" +#include "../encoderB64.hpp" +#include "../message.hpp" +#include "../mailboxList.hpp" +#include "authHelper.hpp" + + +namespace vmime { +namespace messaging { + + +SMTPTransport::SMTPTransport(class session& sess, class authenticator* auth) + : transport(sess, infosInstance(), auth), m_socket(NULL), + m_authentified(false), m_extendedSMTP(false), m_timeoutHandler(NULL) +{ +} + + +SMTPTransport::~SMTPTransport() +{ + if (isConnected()) + disconnect(); + else if (m_socket) + internalDisconnect(); +} + + +const string SMTPTransport::protocolName() const +{ + return "smtp"; +} + + +void SMTPTransport::connect() +{ + if (isConnected()) + throw exceptions::already_connected(); + + const string address = session().properties()[sm_infos.propertyPrefix() + "server.address"]; + const port_t port = session().properties().get(sm_infos.propertyPrefix() + "server.port", sm_infos.defaultPort()); + + // Create the time-out handler + if (session().properties().exists + (sm_infos.propertyPrefix() + "timeout.factory")) + { + timeoutHandlerFactory* tof = platformDependant::getHandler()-> + getTimeoutHandlerFactory(session().properties() + [sm_infos.propertyPrefix() + "timeout.factory"]); + + m_timeoutHandler = tof->create(); + } + + // Create and connect the socket + socketFactory* sf = platformDependant::getHandler()->getSocketFactory + (session().properties().get(sm_infos.propertyPrefix() + "server.socket-factory", string("default"))); + + m_socket = sf->create(); + m_socket->connect(address, port); + + // Connection + // + // eg: C: + // --- S: 220 smtp.domain.com Service ready + + string response; + readResponse(response); + + if (responseCode(response) != 220) + { + internalDisconnect(); + throw exceptions::connection_greeting_error(response); + } + + // Identification + // First, try Extended SMTP (ESMTP) + // + // eg: C: EHLO thismachine.ourdomain.com + // S: 250 OK + + sendRequest("EHLO " + platformDependant::getHandler()->getHostName()); + readResponse(response); + + if (responseCode(response) != 250) + { + // Next, try "Basic" SMTP + // + // eg: C: HELO thismachine.ourdomain.com + // S: 250 OK + + sendRequest("HELO " + platformDependant::getHandler()->getHostName()); + readResponse(response); + + if (responseCode(response) != 250) + { + internalDisconnect(); + throw exceptions::connection_greeting_error(response); + } + + m_extendedSMTP = false; + } + else + { + m_extendedSMTP = true; + } + + // Authentication + if (session().properties().get + (sm_infos.propertyPrefix() + "options.need-authentication", false) == true) + { + if (!m_extendedSMTP) + { + internalDisconnect(); + throw exceptions::command_error("AUTH", "ESMTP not supported."); + } + + const authenticationInfos auth = authenticator().requestAuthInfos(); + bool authentified = false; + + enum AuthMethods + { + First = 0, + CRAM_MD5 = First, + // TODO: more authentication methods... + End + }; + + for (int currentMethod = First ; !authentified ; ++currentMethod) + { + switch (currentMethod) + { + case CRAM_MD5: + { + sendRequest("AUTH CRAM-MD5"); + readResponse(response); + + if (responseCode(response) == 334) + { + encoderB64 base64; + + string challengeB64 = responseText(response); + string challenge, challengeHex; + + { + utility::inputStreamStringAdapter in(challengeB64); + utility::outputStreamStringAdapter out(challenge); + + base64.decode(in, out); + } + + hmac_md5(challenge, auth.password(), challengeHex); + + string decoded = auth.username() + " " + challengeHex; + string encoded; + + { + utility::inputStreamStringAdapter in(decoded); + utility::outputStreamStringAdapter out(encoded); + + base64.encode(in, out); + } + + sendRequest(encoded); + readResponse(response); + + if (responseCode(response) == 235) + { + authentified = true; + } + else + { + internalDisconnect(); + throw exceptions::authentication_error(response); + } + } + + break; + } + case End: + { + // All authentication methods have been tried and + // the server does not understand any. + throw exceptions::authentication_error(response); + } + + } + } + } + + m_authentified = true; +} + + +const bool SMTPTransport::isConnected() const +{ + return (m_socket && m_socket->isConnected() && m_authentified); +} + + +void SMTPTransport::disconnect() +{ + if (!isConnected()) + throw exceptions::not_connected(); + + internalDisconnect(); +} + + +void SMTPTransport::internalDisconnect() +{ + sendRequest("QUIT"); + + m_socket->disconnect(); + + delete (m_socket); + m_socket = NULL; + + delete (m_timeoutHandler); + m_timeoutHandler = NULL; + + m_authentified = false; + m_extendedSMTP = false; +} + + +void SMTPTransport::noop() +{ + m_socket->send("NOOP"); + + string response; + readResponse(response); + + if (responseCode(response) != 250) + throw exceptions::command_error("NOOP", response); +} + + +static void extractMailboxes + (mailboxList& recipients, const addressList& list) +{ + for (addressList::const_iterator it = list.begin() ; + it != list.end() ; ++it) + { + recipients.append((*it)); + } +} + + +void SMTPTransport::send(vmime::message* msg, progressionListener* progress) +{ + // Extract expeditor + mailbox expeditor; + + try + { + const mailboxField& from = dynamic_cast + (msg->header().fields.find(headerField::From)); + expeditor = from.value(); + } + catch (exceptions::no_such_field&) + { + throw exceptions::no_expeditor(); + } + + // Extract recipients + mailboxList recipients; + + try + { + const addressListField& to = dynamic_cast + (msg->header().fields.find(headerField::To)); + extractMailboxes(recipients, to.value()); + } + catch (exceptions::no_such_field&) { } + + try + { + const addressListField& cc = dynamic_cast + (msg->header().fields.find(headerField::Cc)); + extractMailboxes(recipients, cc.value()); + } + catch (exceptions::no_such_field&) { } + + try + { + const addressListField& bcc = dynamic_cast + (msg->header().fields.find(headerField::Bcc)); + extractMailboxes(recipients, bcc.value()); + } + catch (exceptions::no_such_field&) { } + + // Generate the message, "stream" it and delegate the sending + // to the generic send() function. + std::ostringstream oss; + utility::outputStreamAdapter ossAdapter(oss); + + msg->generate(ossAdapter); + + const string& str(oss.str()); + + utility::inputStreamStringAdapter isAdapter(str); + + send(expeditor, recipients, isAdapter, str.length(), progress); +} + + +void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients, + utility::inputStream& is, const utility::stream::size_type size, + progressionListener* progress) +{ + // If no recipient/expeditor was found, throw an exception + if (recipients.empty()) + throw exceptions::no_recipient(); + else if (expeditor.empty()) + throw exceptions::no_expeditor(); + + // Emit the "MAIL" command + string response; + + sendRequest("MAIL FROM: <" + expeditor.email() + ">"); + readResponse(response); + + if (responseCode(response) != 250) + { + internalDisconnect(); + throw exceptions::command_error("MAIL", response); + } + + // Emit a "RCPT TO" command for each recipient + for (mailboxList::const_iterator it = recipients.begin() ; + it != recipients.end() ; ++it) + { + sendRequest("RCPT TO: <" + (*it).email() + ">"); + readResponse(response); + + if (responseCode(response) != 250) + { + internalDisconnect(); + throw exceptions::command_error("RCPT TO", response); + } + } + + // Send the message data + sendRequest("DATA"); + readResponse(response); + + if (responseCode(response) != 354) + { + internalDisconnect(); + throw exceptions::command_error("DATA", response); + } + + int current = 0, total = size; + + if (progress) + progress->start(total); + + char buffer[65536]; + + while (!is.eof()) + { + const int read = is.read(buffer, sizeof(buffer)); + + // Transform '.' into '..' at the beginning of a line + char* start = buffer; + char* end = buffer + read; + char* pos = buffer; + + while ((pos = std::find(pos, end, '.')) != end) + { + if (pos > buffer && *(pos - 1) == '\n') + { + m_socket->sendRaw(start, pos - start); + m_socket->sendRaw(".", 1); + + start = pos; + } + + ++pos; + } + + // Send the remaining data + m_socket->sendRaw(start, end - start); + + current += read; + + // Notify progression + if (progress) + { + total = std::max(total, current); + progress->progress(current, total); + } + } + + if (progress) + progress->stop(total); + + m_socket->sendRaw("\r\n.\r\n", 5); + readResponse(response); + + if (responseCode(response) != 250) + { + internalDisconnect(); + throw exceptions::command_error("DATA", response); + } +} + + +void SMTPTransport::sendRequest(const string& buffer, const bool end) +{ + m_socket->send(buffer); + if (end) m_socket->send("\r\n"); +} + + +const int SMTPTransport::responseCode(const string& response) +{ + int code = 0; + + if (response.length() >= 3) + { + code = (response[0] - '0') * 100 + + (response[1] - '0') * 10 + + (response[2] - '0'); + } + + return (code); +} + + +const string SMTPTransport::responseText(const string& response) +{ + string text; + + std::istringstream iss(response); + std::string line; + + while (std::getline(iss, line)) + { + if (line.length() >= 4) + text += line.substr(4); + else + text += line; + + text += "\n"; + } + + return (text); +} + + +void SMTPTransport::readResponse(string& buffer) +{ + bool foundTerminator = false; + + buffer.clear(); + + for ( ; !foundTerminator ; ) + { + // Check whether the time-out delay is elapsed + if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) + { + if (!m_timeoutHandler->handleTimeOut()) + throw exceptions::operation_timed_out(); + } + + // Receive data from the socket + string receiveBuffer; + m_socket->receive(receiveBuffer); + + if (receiveBuffer.empty()) // buffer is empty + { + platformDependant::getHandler()->wait(); + continue; + } + + // We have received data: reset the time-out counter + if (m_timeoutHandler) + m_timeoutHandler->resetTimeOut(); + + // Append the data to the response buffer + buffer += receiveBuffer; + + // Check for terminator string (and strip it if present) + if (buffer.length() >= 2 && buffer[buffer.length() - 1] == '\n') + { + string::size_type p = buffer.length() - 2; + bool end = false; + + for ( ; !end ; --p) + { + if (p == 0 || buffer[p] == '\n') + { + end = true; + + if (p + 4 < buffer.length()) + foundTerminator = true; + } + } + } + } + + // Remove [CR]LF at the end of the response + if (buffer.length() >= 2 && buffer[buffer.length() - 1] == '\n') + { + if (buffer[buffer.length() - 2] == '\r') + buffer.resize(buffer.length() - 2); + else + buffer.resize(buffer.length() - 1); + } +} + + + +// Service infos + +SMTPTransport::_infos SMTPTransport::sm_infos; + + +const port_t SMTPTransport::_infos::defaultPort() const +{ + return (25); +} + + +const string SMTPTransport::_infos::propertyPrefix() const +{ + return "transport.smtp."; +} + + +const std::vector SMTPTransport::_infos::availableProperties() const +{ + std::vector list; + + // SMTP-specific options + list.push_back("options.need-authentication"); + + // Common properties + list.push_back("auth.username"); + list.push_back("auth.password"); + + list.push_back("server.address"); + list.push_back("server.port"); + list.push_back("server.socket-factory"); + + list.push_back("timeout.factory"); + + return (list); +} + + +} // messaging +} // vmime diff --git a/src/messaging/SMTPTransport.hpp b/src/messaging/SMTPTransport.hpp new file mode 100644 index 00000000..fa5abfcf --- /dev/null +++ b/src/messaging/SMTPTransport.hpp @@ -0,0 +1,95 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SMTPTRANSPORT_HPP_INCLUDED +#define VMIME_MESSAGING_SMTPTRANSPORT_HPP_INCLUDED + + +#include "transport.hpp" +#include "socket.hpp" +#include "../config.hpp" +#include "timeoutHandler.hpp" + + +namespace vmime { +namespace messaging { + + +/** SMTP transport service. + */ + +class SMTPTransport : public transport +{ +public: + + SMTPTransport(class session& sess, class authenticator* auth); + ~SMTPTransport(); + + const string protocolName() const; + + static const serviceInfos& infosInstance() { return (sm_infos); } + const serviceInfos& infos() const { return (sm_infos); } + + void connect(); + const bool isConnected() const; + void disconnect(); + + void noop(); + + void send(vmime::message* msg, progressionListener* progress = NULL); + void send(const mailbox& expeditor, const mailboxList& recipients, utility::inputStream& is, const utility::stream::size_type size, progressionListener* progress = NULL); + +private: + + static const int responseCode(const string& response); + static const string responseText(const string& response); + + void sendRequest(const string& buffer, const bool end = true); + + void readResponse(string& buffer); + + void internalDisconnect(); + + socket* m_socket; + bool m_authentified; + bool m_extendedSMTP; + + timeoutHandler* m_timeoutHandler; + + + // Service infos + class _infos : public serviceInfos + { + public: + + const port_t defaultPort() const; + + const string propertyPrefix() const; + const std::vector availableProperties() const; + }; + + static _infos sm_infos; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SMTPTRANSPORT_HPP_INCLUDED diff --git a/src/messaging/authHelper.cpp b/src/messaging/authHelper.cpp new file mode 100644 index 00000000..05c77d8a --- /dev/null +++ b/src/messaging/authHelper.cpp @@ -0,0 +1,105 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "authHelper.hpp" + +#include "../config.hpp" +#include "../utility/md5.hpp" + + +namespace vmime { +namespace messaging { + + +// +// This code is based on the Sample Code published in the Appendix of +// the RFC-2104: "HMAC: Keyed-Hashing for Message Authentication". +// + +void hmac_md5(const string& text, const string& key, string& hexDigest) +{ + vmime_uint8 digest[16]; + + unsigned char ipad[65]; // inner padding - key XORd with ipad + unsigned char opad[65]; // outer padding - key XORd with opad + + unsigned char tkey[16]; + int tkeyLen; + + // If key is longer than 64 bytes reset it to key = MD5(key) + if (key.length() > 64) + { + utility::md5 keyMD5; + keyMD5.update((vmime_uint8*) key.data(), key.length()); + + std::copy(keyMD5.hash(), keyMD5.hash() + 16, tkey); + tkeyLen = 16; + } + else + { + std::copy(key.begin(), key.end(), tkey); + tkeyLen = key.length(); + } + + // + // the HMAC_MD5 transform looks like: + // + // MD5(K XOR opad, MD5(K XOR ipad, text)) + // + // where K is an n byte key + // ipad is the byte 0x36 repeated 64 times + // + // opad is the byte 0x5c repeated 64 times + // and text is the data being protected + // + + // Start out by storing key in pads + std::fill(ipad, ipad + sizeof(ipad), 0); + std::fill(opad, opad + sizeof(opad), 0); + + std::copy(tkey, tkey + tkeyLen, ipad); + std::copy(tkey, tkey + tkeyLen, opad); + + // XOR key with ipad and opad values + for (int i = 0 ; i < 64 ; ++i) + { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + // Perform inner MD5 + utility::md5 innerMD5; + innerMD5.update(ipad, 64); + innerMD5.update(text); + + std::copy(innerMD5.hash(), innerMD5.hash() + 16, digest); + + // Perform outer MD5 + utility::md5 outerMD5; + outerMD5.update(opad, 64); + outerMD5.update(digest, 16); + + //std::copy(outerMD5.hash(), outerMD5.hash() + 16, digest); + + hexDigest = outerMD5.hex(); +} + + +} // messaging +} // vmime diff --git a/src/messaging/authHelper.hpp b/src/messaging/authHelper.hpp new file mode 100644 index 00000000..1fad3ced --- /dev/null +++ b/src/messaging/authHelper.hpp @@ -0,0 +1,38 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_AUTHHELPER_HPP_INCLUDED +#define VMIME_MESSAGING_AUTHHELPER_HPP_INCLUDED + + +#include "../types.hpp" + + +namespace vmime { +namespace messaging { + + +void hmac_md5(const string& text, const string& key, string& hexDigest); + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_AUTHHELPER_HPP_INCLUDED diff --git a/src/messaging/authenticationInfos.cpp b/src/messaging/authenticationInfos.cpp new file mode 100644 index 00000000..3c5acca7 --- /dev/null +++ b/src/messaging/authenticationInfos.cpp @@ -0,0 +1,40 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "authenticationInfos.hpp" + + +namespace vmime { +namespace messaging { + + +authenticationInfos::authenticationInfos(const string& username, const string& password) + : m_username(username), m_password(password) +{ +} + + +authenticationInfos::authenticationInfos(const authenticationInfos& infos) + : m_username(infos.m_username), m_password(infos.m_password) +{ +} + + +} // messaging +} // vmime diff --git a/src/messaging/authenticationInfos.hpp b/src/messaging/authenticationInfos.hpp new file mode 100644 index 00000000..fd2da2fa --- /dev/null +++ b/src/messaging/authenticationInfos.hpp @@ -0,0 +1,64 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_AUTHENTICATIONINFOS_HPP_INCLUDED +#define VMIME_MESSAGING_AUTHENTICATIONINFOS_HPP_INCLUDED + + +#include "../types.hpp" + + +namespace vmime { +namespace messaging { + + +/** This class encapsulates user credentials. + */ + +class authenticationInfos +{ +public: + + authenticationInfos(const string& username, const string& password); + authenticationInfos(const authenticationInfos& infos); + + /** Return the user account name. + * + * @return account name + */ + const string& username() const { return (m_username); } + + /** Return the user account password. + * + * @return account password + */ + const string& password() const { return (m_password); } + +private: + + string m_username; + string m_password; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_AUTHENTICATIONINFOS_HPP_INCLUDED diff --git a/src/messaging/authenticator.cpp b/src/messaging/authenticator.cpp new file mode 100644 index 00000000..91e488f8 --- /dev/null +++ b/src/messaging/authenticator.cpp @@ -0,0 +1,33 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "authenticator.hpp" + + +namespace vmime { +namespace messaging { + + +authenticator::~authenticator() +{ +} + + +} // messaging +} // vmime diff --git a/src/messaging/authenticator.hpp b/src/messaging/authenticator.hpp new file mode 100644 index 00000000..2faed34c --- /dev/null +++ b/src/messaging/authenticator.hpp @@ -0,0 +1,53 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_AUTHENTICATOR_HPP_INCLUDED +#define VMIME_MESSAGING_AUTHENTICATOR_HPP_INCLUDED + + +#include "authenticationInfos.hpp" + + +namespace vmime { +namespace messaging { + + +/** This class is used to obtain user credentials. + */ + +class authenticator +{ +public: + + virtual ~authenticator(); + + /** Called when the service needs to retrieve user credentials. + * It should return the user account name and password. + * + * @return user credentials (user name and password) + */ + virtual const authenticationInfos requestAuthInfos() const = 0; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_AUTHENTICATOR_HPP_INCLUDED diff --git a/src/messaging/builtinServices.inl b/src/messaging/builtinServices.inl new file mode 100644 index 00000000..f1934ff9 --- /dev/null +++ b/src/messaging/builtinServices.inl @@ -0,0 +1,46 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#define REGISTER_SERVICE(p_class, p_name) \ + vmime::messaging::service::initializer p_name(#p_name) + + +#if VMIME_BUILTIN_MESSAGING_PROTO_POP3 + #include "POP3Store.hpp" + REGISTER_SERVICE(POP3Store, pop3); +#endif + + +#if VMIME_BUILTIN_MESSAGING_PROTO_SMTP + #include "SMTPTransport.hpp" + REGISTER_SERVICE(SMTPTransport, smtp); +#endif + + +#if VMIME_BUILTIN_MESSAGING_PROTO_IMAP + #include "IMAPStore.hpp" + REGISTER_SERVICE(IMAPStore, imap); +#endif + + +#if VMIME_BUILTIN_MESSAGING_PROTO_MAILDIR + #include "maildirStore.hpp" + REGISTER_SERVICE(maildirStore, maildir); +#endif + diff --git a/src/messaging/defaultAuthenticator.cpp b/src/messaging/defaultAuthenticator.cpp new file mode 100644 index 00000000..c69b8c3e --- /dev/null +++ b/src/messaging/defaultAuthenticator.cpp @@ -0,0 +1,41 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "defaultAuthenticator.hpp" + + +namespace vmime { +namespace messaging { + + +defaultAuthenticator::defaultAuthenticator(const propertySet& props, const string& prefix) + : m_props(props), m_prefix(prefix) +{ +} + + +const authenticationInfos defaultAuthenticator::requestAuthInfos() const +{ + return (authenticationInfos + (m_props[m_prefix + "auth.username"], m_props[m_prefix + "auth.password"])); +} + + +} // messaging +} // vmime diff --git a/src/messaging/defaultAuthenticator.hpp b/src/messaging/defaultAuthenticator.hpp new file mode 100644 index 00000000..9480ec56 --- /dev/null +++ b/src/messaging/defaultAuthenticator.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_DEFAULTAUTHENTICATOR_HPP_INCLUDED +#define VMIME_MESSAGING_DEFAULTAUTHENTICATOR_HPP_INCLUDED + + +#include "authenticator.hpp" +#include "../propertySet.hpp" + + +namespace vmime { +namespace messaging { + + +/** An auhenticator that simply returns the credentials set in the + * session properties (named 'username' and 'password'). + */ + +class defaultAuthenticator : public authenticator +{ +public: + + defaultAuthenticator(const propertySet& props, const string& prefix); + +private: + + const propertySet& m_props; + const string m_prefix; + + const authenticationInfos requestAuthInfos() const; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_DEFAULTAUTHENTICATOR_HPP_INCLUDED diff --git a/src/messaging/events.cpp b/src/messaging/events.cpp new file mode 100644 index 00000000..d117cc82 --- /dev/null +++ b/src/messaging/events.cpp @@ -0,0 +1,110 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "events.hpp" + +#include + + +namespace vmime { +namespace messaging { +namespace events { + + +// +// messageCountEvent +// + +messageCountEvent::messageCountEvent + (class folder* folder, const Types type, const std::vector & nums) + : m_folder(folder), m_type(type) +{ + m_nums.resize(nums.size()); + std::copy(nums.begin(), nums.end(), m_nums.begin()); +} + + +const folder* messageCountEvent::folder() const { return (const_cast (m_folder)); } +const messageCountEvent::Types messageCountEvent::type() const { return (m_type); } +const std::vector & messageCountEvent::numbers() const { return (m_nums); } + + +void messageCountEvent::dispatch(messageCountListener* listener) const +{ + if (m_type == TYPE_ADDED) + listener->messagesAdded(*this); + else + listener->messagesRemoved(*this); +} + + +// +// messageChangedEvent +// + +messageChangedEvent::messageChangedEvent + (class folder* folder, const Types type, const std::vector & nums) + : m_folder(folder), m_type(type) +{ + m_nums.resize(nums.size()); + std::copy(nums.begin(), nums.end(), m_nums.begin()); +} + + +const folder* messageChangedEvent::folder() const { return (const_cast (m_folder)); } +const messageChangedEvent::Types messageChangedEvent::type() const { return (m_type); } +const std::vector & messageChangedEvent::numbers() const { return (m_nums); } + + +void messageChangedEvent::dispatch(messageChangedListener* listener) const +{ + listener->messageChanged(*this); +} + + +// +// folderEvent +// + +folderEvent::folderEvent + (class folder* folder, const Types type, + const utility::path& oldPath, const utility::path& newPath) + : m_folder(folder), m_type(type), m_oldPath(oldPath), m_newPath(newPath) +{ +} + + +const class folder* folderEvent::folder() const { return (m_folder); } +const folderEvent::Types folderEvent::type() const { return (m_type); } + + +void folderEvent::dispatch(class folderListener* listener) const +{ + switch (m_type) + { + case TYPE_CREATED: listener->folderCreated(*this); break; + case TYPE_RENAMED: listener->folderRenamed(*this); break; + case TYPE_DELETED: listener->folderDeleted(*this); break; + } +} + + +} // events +} // messaging +} // vmime diff --git a/src/messaging/events.hpp b/src/messaging/events.hpp new file mode 100644 index 00000000..d51ca2d0 --- /dev/null +++ b/src/messaging/events.hpp @@ -0,0 +1,174 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_EVENTS_HPP_INCLUDED +#define VMIME_MESSAGING_EVENTS_HPP_INCLUDED + + +#include + +#include "../utility/path.hpp" + + +namespace vmime { +namespace messaging { + +class folder; + +namespace events { + + +// +// messageCountEvent +// + +class messageCountEvent +{ +public: + + enum Types + { + TYPE_ADDED, // new messages + TYPE_REMOVED // expunged messages: renumbering + }; + + + messageCountEvent(class folder* folder, const Types type, const std::vector & nums); + + const class folder* folder() const; + const Types type() const; + const std::vector & numbers() const; + + void dispatch(class messageCountListener* listener) const; + +private: + + class folder* m_folder; + const Types m_type; + std::vector m_nums; +}; + + +class messageCountListener +{ +protected: + + virtual ~messageCountListener() { } + +public: + + virtual void messagesAdded(const messageCountEvent& event) = 0; + virtual void messagesRemoved(const messageCountEvent& event) = 0; +}; + + +// +// messageChangedEvent +// + +class messageChangedEvent +{ +public: + + enum Types + { + TYPE_FLAGS // flags changed + }; + + + messageChangedEvent(class folder* folder, const Types type, const std::vector & nums); + + const class folder* folder() const; + const Types type() const; + const std::vector & numbers() const; + + void dispatch(class messageChangedListener* listener) const; + +private: + + class folder* m_folder; + const Types m_type; + std::vector m_nums; +}; + + +class messageChangedListener +{ +protected: + + virtual ~messageChangedListener() { } + +public: + + virtual void messageChanged(const messageChangedEvent& event) = 0; +}; + + +// +// folderEvent +// + +class folderEvent +{ +public: + + enum Types + { + TYPE_CREATED, // a folder was created + TYPE_DELETED, // a folder was deleted + TYPE_RENAMED // a folder was renamed + }; + + + folderEvent(class folder* folder, const Types type, const utility::path& oldPath, const utility::path& newPath); + + const class folder* folder() const; + const Types type() const; + + void dispatch(class folderListener* listener) const; + +private: + + class folder* m_folder; + const Types m_type; + const utility::path m_oldPath; + const utility::path m_newPath; +}; + + +class folderListener +{ +protected: + + virtual ~folderListener() { } + +public: + + virtual void folderCreated(const folderEvent& event) = 0; + virtual void folderRenamed(const folderEvent& event) = 0; + virtual void folderDeleted(const folderEvent& event) = 0; +}; + + +} // events +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_EVENTS_HPP_INCLUDED diff --git a/src/messaging/folder.cpp b/src/messaging/folder.cpp new file mode 100644 index 00000000..e508389c --- /dev/null +++ b/src/messaging/folder.cpp @@ -0,0 +1,96 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "folder.hpp" + +#include + + +namespace vmime { +namespace messaging { + + +void folder::addMessageChangedListener(events::messageChangedListener* l) +{ + m_messageChangedListeners.push_back(l); +} + + +void folder::removeMessageChangedListener(events::messageChangedListener* l) +{ + std::remove(m_messageChangedListeners.begin(), m_messageChangedListeners.end(), l); +} + + +void folder::notifyMessageChanged(const events::messageChangedEvent& event) +{ + for (std::list ::iterator + it = m_messageChangedListeners.begin() ; it != m_messageChangedListeners.end() ; ++it) + { + event.dispatch(*it); + } +} + + +void folder::addMessageCountListener(events::messageCountListener* l) +{ + m_messageCountListeners.push_back(l); +} + + +void folder::removeMessageCountListener(events::messageCountListener* l) +{ + std::remove(m_messageCountListeners.begin(), m_messageCountListeners.end(), l); +} + + +void folder::notifyMessageCount(const events::messageCountEvent& event) +{ + for (std::list ::iterator + it = m_messageCountListeners.begin() ; it != m_messageCountListeners.end() ; ++it) + { + event.dispatch(*it); + } +} + + +void folder::addFolderListener(events::folderListener* l) +{ + m_folderListeners.push_back(l); +} + + +void folder::removeFolderListener(events::folderListener* l) +{ + std::remove(m_folderListeners.begin(), m_folderListeners.end(), l); +} + + +void folder::notifyFolder(const events::folderEvent& event) +{ + for (std::list ::iterator + it = m_folderListeners.begin() ; it != m_folderListeners.end() ; ++it) + { + event.dispatch(*it); + } +} + + +} // messaging +} // vmime diff --git a/src/messaging/folder.hpp b/src/messaging/folder.hpp new file mode 100644 index 00000000..574ac1b0 --- /dev/null +++ b/src/messaging/folder.hpp @@ -0,0 +1,373 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_FOLDER_HPP_INCLUDED +#define VMIME_MESSAGING_FOLDER_HPP_INCLUDED + + +#include + +#include "../types.hpp" +#include "../dateTime.hpp" +#include "progressionListener.hpp" +#include "message.hpp" +#include "../message.hpp" +#include "events.hpp" +#include "../utility/path.hpp" +#include "../utility/stream.hpp" + + +namespace vmime { +namespace messaging { + + +/** Abstract representation of a folder in a message store. + */ + +class folder +{ +protected: + + folder(const folder&) { } + folder() { } + +public: + + virtual ~folder() { } + + /** Type used for fully qualified path name of a folder. + */ + typedef vmime::utility::path path; + + + /** Open mode. + */ + enum Modes + { + MODE_READ_ONLY, /**< Read-only mode (no modification to folder or messages is possible). */ + MODE_READ_WRITE /**< Full access mode (read and write). */ + }; + + /** Folder types. + */ + enum Types + { + TYPE_CONTAINS_FOLDERS = (1 << 0), /**< Folder can contain folders. */ + TYPE_CONTAINS_MESSAGES = (1 << 1), /**< Folder can contain messages. */ + + TYPE_UNDEFINED = 9999 /**< Used internally (this should not be returned + by the type() function). */ + }; + + /** Folder flags. + */ + enum Flags + { + FLAG_CHILDREN = (1 << 0), /**< Folder contains subfolders. */ + FLAG_NO_OPEN = (1 << 1), /**< Folder cannot be open. */ + + FLAG_UNDEFINED = 9999 /**< Used internally (this should not be returned + by the type() function). */ + }; + + /** Return the type of this folder. + * + * @return folder type (see folder::Types) + */ + virtual const int type() = 0; + + /** Return the flags of this folder. + * + * @return folder flags (see folder::Flags) + */ + virtual const int flags() = 0; + + /** Return the mode in which the folder has been open. + * + * @return folder opening mode (see folder::Modes) + */ + virtual const int mode() const = 0; + + /** Return the name of this folder. + * + * @return folder name + */ + virtual const folder::path::component name() const = 0; + + /** Return the fully qualified path name of this folder. + * + * @return absolute path of the folder + */ + virtual const folder::path fullPath() const = 0; + + /** Open this folder. + * + * @param mode open mode (see folder::Modes) + * @param failIfModeIsNotAvailable if set to false and if the requested mode + * is not available, a more restricted mode will be selected automatically. + * If set to true and if the requested mode is not available, the opening + * will fail. + */ + virtual void open(const int mode, bool failIfModeIsNotAvailable = false) = 0; + + /** Close this folder. + * + * @param expunge if set to true, deleted messages are expunged + */ + virtual void close(const bool expunge) = 0; + + /** Create this folder. + * + * @param type folder type (see folder::Types) + */ + virtual void create(const int type) = 0; + + /** Test whether this folder exists. + * + * @return true if the folder exists, false otherwise + */ + virtual const bool exists() = 0; + + /** Test whether this folder is open. + * + * @return true if the folder is open, false otherwise + */ + virtual const bool isOpen() const = 0; + + /** Get a new reference to a message in this folder. + * + * @param num message sequence number + * @return a new object referencing the specified message + */ + virtual message* getMessage(const int num) = 0; + + /** Get new references to messages in this folder. + * + * @param from sequence number of the first message to get + * @param to sequence number of the last message to get + * @return new objects referencing the specified messages + */ + virtual std::vector getMessages(const int from = 1, const int to = -1) = 0; + + /** Get new references to messages in this folder. + * + * @param nums sequence numbers of the messages to delete + * @return new objects referencing the specified messages + */ + virtual std::vector getMessages(const std::vector & nums) = 0; + + /** Return the number of messages in this folder. + * + * @return number of messages in the folder + */ + virtual const int getMessageCount() = 0; + + /** Get a new reference to a sub-folder in this folder. + * + * @param name sub-folder name + * @return a new object referencing the specified folder + */ + virtual folder* getFolder(const folder::path::component& name) = 0; + + /** Get the list of all sub-folders in this folder. + * + * @param recursive if set to true, all the descendant are returned. + * If set to false, only the direct children are returned. + * @return list of sub-folders + */ + virtual std::vector getFolders(const bool recursive = false) = 0; + + /** Rename (move) this folder to another location. + * + * @param newPath new path of the folder + */ + virtual void rename(const folder::path& newPath) = 0; + + /** Remove a message in this folder. + * + * @param num sequence number of the message to delete + */ + virtual void deleteMessage(const int num) = 0; + + /** Remove one or more messages from this folder. + * + * @param from sequence number of the first message to delete + * @param to sequence number of the last message to delete + */ + virtual void deleteMessages(const int from = 1, const int to = -1) = 0; + + /** Remove one or more messages from this folder. + * + * @param nums sequence numbers of the messages to delete + */ + virtual void deleteMessages(const std::vector & nums) = 0; + + /** Change the flags for one or more messages in this folder. + * + * @param from sequence number of the first message to modify + * @param to sequence number of the last message to modify + * @param flags set of flags (see message::Flags) + * @param mode indicate how to treat old and new flags (see message::FlagsModes) + */ + virtual void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET) = 0; + + /** Change the flags for one or more messages in this folder. + * + * @param nums sequence numbers of the messages to modify + * @param flags set of flags (see message::Flags) + * @param mode indicate how to treat old and new flags (see message::FlagsModes) + */ + virtual void setMessageFlags(const std::vector & nums, const int flags, const int mode = message::FLAG_MODE_SET) = 0; + + /** Add a message to this folder. + * + * @param msg message to add (data: header + body) + * @param flags flags for the new message + * @param date date/time for the new message (if NULL, the current time is used) + * @param progress progression listener, or NULL if not used + */ + virtual void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL) = 0; + + /** Add a message to this folder. + * + * @param is message to add (data: header + body) + * @param size size of the message to add (in bytes) + * @param flags flags for the new message + * @param date date/time for the new message (if NULL, the current time is used) + * @param progress progression listener, or NULL if not used + */ + virtual void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL) = 0; + + /** Copy a message from this folder to another folder. + * + * @param dest destination folder path + * @param num sequence number of the message to copy + */ + virtual void copyMessage(const folder::path& dest, const int num) = 0; + + /** Copy messages from this folder to another folder. + * + * @param dest destination folder path + * @param from sequence number of the first message to copy + * @param to sequence number of the last message to copy + */ + virtual void copyMessages(const folder::path& dest, const int from = 1, const int to = -1) = 0; + + /** Copy messages from this folder to another folder. + * + * @param dest destination folder path + * @param nums sequence numbers of the messages to copy + */ + virtual void copyMessages(const folder::path& dest, const std::vector & nums) = 0; + + /** Request folder status without opening it. + * + * @param count will receive the number of messages in the folder + * @param unseen will receive the number of unseen messages in the folder + */ + virtual void status(int& count, int& unseen) = 0; + + /** Expunge deleted messages. + */ + virtual void expunge() = 0; + + /** Return a new folder objet referencing the parent folder of this folder. + * + * @return parent folder object + */ + virtual folder* getParent() = 0; + + /** Return a reference to the store to which this folder belongs. + * + * @return the store object to which this folder is attached + */ + virtual const class store& store() const = 0; + + /** Return a reference to the store to which this folder belongs. + * + * @return the store object to which this folder is attached + */ + virtual class store& store() = 0; + + /** Possible fetchable objects. + */ + enum FetchOptions + { + FETCH_ENVELOPE = (1 << 0), /**< Fetch sender, recipients, date, subject. */ + FETCH_STRUCTURE = (1 << 1), /**< Fetch structure (body parts). */ + FETCH_CONTENT_INFO = (1 << 2), /**< Fetch top-level content type. */ + FETCH_FLAGS = (1 << 3), /**< Fetch message flags. */ + FETCH_SIZE = (1 << 4), /**< Fetch message size (exact or estimated). */ + FETCH_FULL_HEADER = (1 << 5), /**< Fetch full RFC-[2]822 header. */ + FETCH_UID = (1 << 6), /**< Fetch unique identifier (protocol specific). */ + + FETCH_CUSTOM = (1 << 16) /**< Reserved for future use. */ + }; + + /** Fetch objects for the specified messages. + * + * @param msg list of message sequence numbers + * @param options objects to fetch (combination of folder::FetchOptions flags) + * @param progress progression listener, or NULL if not used + */ + virtual void fetchMessages(std::vector & msg, const int options, progressionListener* progress = NULL) = 0; + + /** Fetch objects for the specified message. + * + * @param msg the message + * @param options objects to fetch (combination of folder::FetchOptions flags) + */ + virtual void fetchMessage(message* msg, const int options) = 0; + + /** Return the list of fetchable objects supported by + * the underlying protocol (see folder::FetchOptions). + * + * @return list of supported fetchable objects + */ + virtual const int getFetchCapabilities() const = 0; + + // Event listeners + void addMessageChangedListener(events::messageChangedListener* l); + void removeMessageChangedListener(events::messageChangedListener* l); + + void addMessageCountListener(events::messageCountListener* l); + void removeMessageCountListener(events::messageCountListener* l); + + void addFolderListener(events::folderListener* l); + void removeFolderListener(events::folderListener* l); + +protected: + + void notifyMessageChanged(const events::messageChangedEvent& event); + void notifyMessageCount(const events::messageCountEvent& event); + void notifyFolder(const events::folderEvent& event); + +private: + + std::list m_messageChangedListeners; + std::list m_messageCountListeners; + std::list m_folderListeners; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_FOLDER_HPP_INCLUDED diff --git a/src/messaging/maildirFolder.cpp b/src/messaging/maildirFolder.cpp new file mode 100644 index 00000000..903de91d --- /dev/null +++ b/src/messaging/maildirFolder.cpp @@ -0,0 +1,552 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "maildirFolder.hpp" + +#include "maildirStore.hpp" +#include "maildirMessage.hpp" +#include "maildirUtils.hpp" + +#include "../exception.hpp" +#include "../platformDependant.hpp" + + +namespace vmime { +namespace messaging { + + +maildirFolder::maildirFolder(const folder::path& path, maildirStore* store) + : m_store(store), m_path(path), m_name(path.last()), m_mode(-1), m_open(false) +{ + m_store->registerFolder(this); +} + + +maildirFolder::~maildirFolder() +{ + if (m_store) + { + if (m_open) + close(false); + + m_store->unregisterFolder(this); + } + else if (m_open) + { + close(false); + } +} + + +void maildirFolder::onStoreDisconnected() +{ + m_store = NULL; +} + + +const int maildirFolder::mode() const +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + return (m_mode); +} + + +const int maildirFolder::type() +{ + if (m_path.empty()) + return (TYPE_CONTAINS_FOLDERS); + else + return (TYPE_CONTAINS_FOLDERS | TYPE_CONTAINS_MESSAGES); +} + + +const int maildirFolder::flags() +{ + int flags = 0; + + utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory(); + + utility::auto_ptr rootDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CONTAINER)); + + utility::auto_ptr it = rootDir->getFiles(); + + while (it->hasMoreElements()) + { + utility::auto_ptr file = it->nextElement(); + + if (maildirUtils::isSubfolderDirectory(*file)) + { + flags |= FLAG_CHILDREN; // Contains at least one sub-folder + break; + } + } + + return (flags); +} + + +const folder::path::component maildirFolder::name() const +{ + return (m_name); +} + + +const folder::path maildirFolder::fullPath() const +{ + return (m_path); +} + + +void maildirFolder::open(const int mode, bool /* failIfModeIsNotAvailable */) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (isOpen()) + throw exceptions::illegal_state("Folder is already open"); + else if (!exists()) + throw exceptions::illegal_state("Folder already exists"); + + m_open = true; + m_mode = mode; +} + + +void maildirFolder::close(const bool expunge) +{ + // TODO +} + + +void maildirFolder::onClose() +{ + // TODO +} + + +void maildirFolder::create(const int type) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + else if (isOpen()) + throw exceptions::illegal_state("Folder is open"); + else if (exists()) + throw exceptions::illegal_state("Folder already exists"); + + // Folder name cannot start with '.' + if (!m_path.empty()) + { + const path::component& comp = m_path.last(); + + const int length = comp.buffer().length(); + int pos = 0; + + while ((pos < length) && (comp.buffer()[pos] == '.')) + ++pos; + + if (pos != 0) + throw exceptions::invalid_folder_name("Name cannot start with '.'"); + } + + // Create directory on file system + try + { + utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory(); + + utility::auto_ptr rootDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_ROOT)); + + utility::auto_ptr newDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_NEW)); + utility::auto_ptr tmpDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_TMP)); + utility::auto_ptr curDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CUR)); + + rootDir->createDirectory(true); + + newDir->createDirectory(false); + tmpDir->createDirectory(false); + curDir->createDirectory(false); + } + catch (exceptions::filesystem_exception& e) + { + throw exceptions::command_error("CREATE", e.what(), "File system exception"); + } + + // Notify folder created + events::folderEvent event(this, events::folderEvent::TYPE_CREATED, m_path, m_path); + notifyFolder(event); +} + + +const bool maildirFolder::exists() +{ + utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory(); + + utility::auto_ptr rootDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_ROOT)); + + utility::auto_ptr newDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_NEW)); + utility::auto_ptr tmpDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_TMP)); + utility::auto_ptr curDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CUR)); + + return (rootDir->exists() && rootDir->isDirectory() && + newDir->exists() && newDir->isDirectory() && + tmpDir->exists() && tmpDir->isDirectory() && + curDir->exists() && curDir->isDirectory()); +} + + +const bool maildirFolder::isOpen() const +{ + return (m_open); +} + + +void maildirFolder::scanFolder() +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + try + { + utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory(); + + utility::auto_ptr newDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_NEW)); + utility::auto_ptr curDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CUR)); + + // Unread messages (new/) + utility::auto_ptr nit = newDir->getFiles(); + std::vector unreadMessageFilenames; + + while (nit->hasMoreElements()) + { + utility::auto_ptr file = nit->nextElement(); + unreadMessageFilenames.push_back(file->fullPath().last()); + } + + // Seen messages (cur/) + utility::auto_ptr cit = curDir->getFiles(); + std::vector messageFilenames; + + while (cit->hasMoreElements()) + { + utility::auto_ptr file = cit->nextElement(); + messageFilenames.push_back(file->fullPath().last()); + } + + // TODO: update m_messageFilenames + // TODO: what to do with files which name has changed? (flag change, message deletion...) + + m_unreadMessageCount = unreadMessageFilenames.size(); + m_messageCount = messageFilenames.size(); + } + catch (exceptions::filesystem_exception&) + { + // Should not happen... + } + + /* + int m_unreadMessageCount; + int m_messageCount; + + std::vector m_unreadMessageFilenames; + std::vector m_messageFilenames; + + if (0) + { + m_messageFilenames.clear(); + + for (...) + { + m_messageFilenames.push_back(...); + } + } + */ +} + + +message* maildirFolder::getMessage(const int num) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + if (num < 1 || num > m_messageCount) + throw exceptions::message_not_found(); + + return new maildirMessage(this, num); +} + + +std::vector maildirFolder::getMessages(const int from, const int to) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + std::vector v; + + for (int i = from ; i <= to ; ++i) + v.push_back(new maildirMessage(this, i)); + + return (v); +} + + +std::vector maildirFolder::getMessages(const std::vector & nums) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + std::vector v; + + for (std::vector ::const_iterator it = nums.begin() ; it != nums.end() ; ++it) + v.push_back(new maildirMessage(this, *it)); + + return (v); +} + + +const int maildirFolder::getMessageCount() +{ + return (m_messageCount); +} + + +folder* maildirFolder::getFolder(const folder::path::component& name) +{ + if (!m_store) + throw exceptions::illegal_state("Store disconnected"); + + return new maildirFolder(m_path / name, m_store); +} + + +std::vector maildirFolder::getFolders(const bool recursive) +{ + if (!isOpen() && !m_store) + throw exceptions::illegal_state("Store disconnected"); + + std::vector list; + + try + { + listFolders(list, recursive); + } + catch (std::exception&) + { + for (std::vector ::iterator it = list.begin() ; it != list.end() ; ++it) + delete (*it); + + throw; + } + + return (list); +} + + +void maildirFolder::listFolders(std::vector & list, const bool recursive) +{ + try + { + utility::fileSystemFactory* fsf = platformDependant::getHandler()->getFileSystemFactory(); + + utility::auto_ptr rootDir = fsf->create + (maildirUtils::getFolderFSPath(m_store, m_path, maildirUtils::FOLDER_PATH_CONTAINER)); + utility::auto_ptr it = rootDir->getFiles(); + + while (it->hasMoreElements()) + { + utility::auto_ptr file = it->nextElement(); + + if (maildirUtils::isSubfolderDirectory(*file)) + { + const utility::path subPath = m_path / file->fullPath().last(); + maildirFolder* subFolder = new maildirFolder(subPath, m_store); + + list.push_back(subFolder); + + if (recursive) + subFolder->listFolders(list, true); + } + } + } + catch (exceptions::filesystem_exception& e) + { + throw exceptions::command_error("LIST", e.what()); + } +} + + +void maildirFolder::rename(const folder::path& newPath) +{ + // TODO +} + + +void maildirFolder::deleteMessage(const int num) +{ + // TODO +} + + +void maildirFolder::deleteMessages(const int from, const int to) +{ + // TODO +} + + +void maildirFolder::deleteMessages(const std::vector & nums) +{ + // TODO +} + + +void maildirFolder::setMessageFlags + (const int from, const int to, const int flags, const int mode) +{ + // TODO +} + + +void maildirFolder::setMessageFlags + (const std::vector & nums, const int flags, const int mode) +{ + // TODO +} + + +void maildirFolder::addMessage(vmime::message* msg, const int flags, + vmime::datetime* date, progressionListener* progress) +{ + // TODO +} + + +void maildirFolder::addMessage(utility::inputStream& is, const int size, + const int flags, vmime::datetime* date, progressionListener* progress) +{ + // TODO +} + + +void maildirFolder::copyMessage(const folder::path& dest, const int num) +{ + // TODO +} + + +void maildirFolder::copyMessages(const folder::path& dest, const int from, const int to) +{ + // TODO +} + + +void maildirFolder::copyMessages(const folder::path& dest, const std::vector & nums) +{ + // TODO +} + + +void maildirFolder::status(int& count, int& unseen) +{ + const int oldCount = m_messageCount; + + scanFolder(); + + count = m_messageCount; + unseen = m_unreadMessageCount; + + // Notify message count changed (new messages) + if (count > oldCount) + { + std::vector nums; + nums.reserve(count - oldCount); + + for (int i = oldCount + 1, j = 0 ; i <= count ; ++i, ++j) + nums[j] = i; + + events::messageCountEvent event(this, events::messageCountEvent::TYPE_ADDED, nums); + + for (std::list ::iterator it = m_store->m_folders.begin() ; + it != m_store->m_folders.end() ; ++it) + { + if ((*it)->fullPath() == m_path) + { + (*it)->m_messageCount = count; + (*it)->notifyMessageCount(event); + } + } + } +} + + +void maildirFolder::expunge() +{ + // TODO +} + + +folder* maildirFolder::getParent() +{ + return (m_path.empty() ? NULL : new maildirFolder(m_path.parent(), m_store)); +} + + +const class store& maildirFolder::store() const +{ + return (*m_store); +} + + +class store& maildirFolder::store() +{ + return (*m_store); +} + + +void maildirFolder::fetchMessages(std::vector & msg, + const int options, progressionListener* progress) +{ + // TODO +} + + +void maildirFolder::fetchMessage(message* msg, const int options) +{ + // TODO +} + + +const int maildirFolder::getFetchCapabilities() const +{ + return (FETCH_ENVELOPE | FETCH_STRUCTURE | FETCH_CONTENT_INFO | + FETCH_FLAGS | FETCH_SIZE | FETCH_FULL_HEADER | FETCH_UID); +} + + +} // messaging +} // vmime diff --git a/src/messaging/maildirFolder.hpp b/src/messaging/maildirFolder.hpp new file mode 100644 index 00000000..37578157 --- /dev/null +++ b/src/messaging/maildirFolder.hpp @@ -0,0 +1,144 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_MAILDIRFOLDER_HPP_INCLUDED +#define VMIME_MESSAGING_MAILDIRFOLDER_HPP_INCLUDED + + +#include +#include + +#include "../types.hpp" +#include "folder.hpp" + + +namespace vmime { +namespace messaging { + + +class maildirStore; + + +/** maildir folder implementation. + */ + +class maildirFolder : public folder +{ +protected: + + friend class maildirStore; + friend class maildirMessage; + + + maildirFolder(const folder::path& path, maildirStore* store); + maildirFolder(const maildirFolder&) : folder() { } + + ~maildirFolder(); + +public: + + const int mode() const; + + const int type(); + + const int flags(); + + const folder::path::component name() const; + const folder::path fullPath() const; + + void open(const int mode, bool failIfModeIsNotAvailable = false); + void close(const bool expunge); + void create(const int type); + + const bool exists(); + + const bool isOpen() const; + + message* getMessage(const int num); + std::vector getMessages(const int from = 1, const int to = -1); + std::vector getMessages(const std::vector & nums); + const int getMessageCount(); + + folder* getFolder(const folder::path::component& name); + std::vector getFolders(const bool recursive = false); + + void rename(const folder::path& newPath); + + void deleteMessage(const int num); + void deleteMessages(const int from = 1, const int to = -1); + void deleteMessages(const std::vector & nums); + + void setMessageFlags(const int from, const int to, const int flags, const int mode = message::FLAG_MODE_SET); + void setMessageFlags(const std::vector & nums, const int flags, const int mode = message::FLAG_MODE_SET); + + void addMessage(vmime::message* msg, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + void addMessage(utility::inputStream& is, const int size, const int flags = message::FLAG_UNDEFINED, vmime::datetime* date = NULL, progressionListener* progress = NULL); + + void copyMessage(const folder::path& dest, const int num); + void copyMessages(const folder::path& dest, const int from = 1, const int to = -1); + void copyMessages(const folder::path& dest, const std::vector & nums); + + void status(int& count, int& unseen); + + void expunge(); + + folder* getParent(); + + const class store& store() const; + class store& store(); + + + void fetchMessages(std::vector & msg, const int options, progressionListener* progress = NULL); + void fetchMessage(message* msg, const int options); + + const int getFetchCapabilities() const; + +private: + + maildirStore* m_store; + + folder::path m_path; + folder::path::component m_name; + + int m_mode; + bool m_open; + + int m_unreadMessageCount; + int m_messageCount; + + std::vector m_unreadMessageFilenames; + std::vector m_messageFilenames; + + void scanFolder(); + + void listFolders(std::vector & list, const bool recursive); + + + + void onStoreDisconnected(); + + void onClose(); +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_MAILDIRFOLDER_HPP_INCLUDED diff --git a/src/messaging/maildirMessage.cpp b/src/messaging/maildirMessage.cpp new file mode 100644 index 00000000..c14831c3 --- /dev/null +++ b/src/messaging/maildirMessage.cpp @@ -0,0 +1,28 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "maildirMessage.hpp" + + +namespace vmime { +namespace messaging { + + +} // messaging +} // vmime diff --git a/src/messaging/maildirMessage.hpp b/src/messaging/maildirMessage.hpp new file mode 100644 index 00000000..3ea6afbf --- /dev/null +++ b/src/messaging/maildirMessage.hpp @@ -0,0 +1,78 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_MAILDIRMESSAGE_HPP_INCLUDED +#define VMIME_MESSAGING_MAILDIRMESSAGE_HPP_INCLUDED + + +#include "message.hpp" +#include "folder.hpp" + + +namespace vmime { +namespace messaging { + + +class maildirFolder; + + +/** maildir message implementation. + */ + +class maildirMessage : public message +{ + friend class maildirFolder; + +protected: + + maildirMessage(maildirFolder* folder, const int num); + maildirMessage(const maildirMessage&) : message() { } + + ~maildirMessage(); + +public: + + const int number() const; + + const uid uniqueId() const; + + const int size() const; + + const bool isExpunged() const; + + const class structure& structure() const; + class structure& structure(); + + const class header& header() const; + + const int flags() const; + void setFlags(const int flags, const int mode = FLAG_MODE_SET); + + void extract(utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + void extractPart(const part& p, utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const; + + void fetchPartHeader(part& p); +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_MAILDIRMESSAGE_HPP_INCLUDED diff --git a/src/messaging/maildirStore.cpp b/src/messaging/maildirStore.cpp new file mode 100644 index 00000000..2e125109 --- /dev/null +++ b/src/messaging/maildirStore.cpp @@ -0,0 +1,165 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "maildirStore.hpp" + +#include "maildirFolder.hpp" + +#include "../exception.hpp" +#include "../platformDependant.hpp" + + +namespace vmime { +namespace messaging { + + +maildirStore::maildirStore(class session& sess, class authenticator* auth) + : store(sess, infosInstance(), auth), m_connected(false) +{ +} + + +maildirStore::~maildirStore() +{ + if (isConnected()) + disconnect(); +} + + +const string maildirStore::protocolName() const +{ + return "maildir"; +} + + +folder* maildirStore::getRootFolder() +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new maildirFolder(folder::path(), this); +} + + +folder* maildirStore::getDefaultFolder() +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new maildirFolder(folder::path::component("inbox"), this); +} + + +folder* maildirStore::getFolder(const folder::path& path) +{ + if (!isConnected()) + throw exceptions::illegal_state("Not connected"); + + return new maildirFolder(path, this); +} + + +void maildirStore::connect() +{ + if (isConnected()) + throw exceptions::already_connected(); + + m_fsPath = platformDependant::getHandler()->getFileSystemFactory()->stringToPath + (session().properties()[infos().propertyPrefix() + "server.path"]); + + m_connected = true; +} + + +const bool maildirStore::isConnected() const +{ + return (m_connected); +} + + +void maildirStore::disconnect() +{ + for (std::list ::iterator it = m_folders.begin() ; + it != m_folders.end() ; ++it) + { + (*it)->onStoreDisconnected(); + } + + m_folders.clear(); + + m_connected = false; +} + + +void maildirStore::noop() +{ + // Nothing to do. +} + + +void maildirStore::registerFolder(maildirFolder* folder) +{ + m_folders.push_back(folder); +} + + +void maildirStore::unregisterFolder(maildirFolder* folder) +{ + std::list ::iterator it = std::find(m_folders.begin(), m_folders.end(), folder); + if (it != m_folders.end()) m_folders.erase(it); +} + + +const utility::path& maildirStore::getFileSystemPath() const +{ + return (m_fsPath); +} + + + + +// Service infos + +maildirStore::_infos maildirStore::sm_infos; + + +const port_t maildirStore::_infos::defaultPort() const +{ + return (0); +} + + +const string maildirStore::_infos::propertyPrefix() const +{ + return "store.maildir."; +} + + +const std::vector maildirStore::_infos::availableProperties() const +{ + std::vector list; + + list.push_back("server.path"); + + return (list); +} + + +} // messaging +} // vmime diff --git a/src/messaging/maildirStore.hpp b/src/messaging/maildirStore.hpp new file mode 100644 index 00000000..62acb13e --- /dev/null +++ b/src/messaging/maildirStore.hpp @@ -0,0 +1,102 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_MAILDIRSTORE_HPP_INCLUDED +#define VMIME_MESSAGING_MAILDIRSTORE_HPP_INCLUDED + + +#include "store.hpp" +#include "socket.hpp" +#include "folder.hpp" +#include "../config.hpp" + +#include "utility/file.hpp" + +#include + + +namespace vmime { +namespace messaging { + + +class maildirFolder; + + +/** maildir store service. + */ + +class maildirStore : public store +{ + friend class maildirFolder; + +public: + + maildirStore(class session& sess, class authenticator* auth); + ~maildirStore(); + + const string protocolName() const; + + folder* getDefaultFolder(); + folder* getRootFolder(); + folder* getFolder(const folder::path& path); + + static const serviceInfos& infosInstance() { return (sm_infos); } + const serviceInfos& infos() const { return (sm_infos); } + + void connect(); + const bool isConnected() const; + void disconnect(); + + void noop(); + + const utility::path& getFileSystemPath() const; + +private: + + void registerFolder(maildirFolder* folder); + void unregisterFolder(maildirFolder* folder); + + + std::list m_folders; + + bool m_connected; + + utility::path m_fsPath; + + + // Service infos + class _infos : public serviceInfos + { + public: + + const port_t defaultPort() const; + + const string propertyPrefix() const; + const std::vector availableProperties() const; + }; + + static _infos sm_infos; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_MAILDIRSTORE_HPP_INCLUDED diff --git a/src/messaging/maildirUtils.cpp b/src/messaging/maildirUtils.cpp new file mode 100644 index 00000000..28df6902 --- /dev/null +++ b/src/messaging/maildirUtils.cpp @@ -0,0 +1,87 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "maildirUtils.hpp" +#include "maildirStore.hpp" + + +namespace vmime { +namespace messaging { + + +const vmime::word maildirUtils::TMP_DIR("tmp"); // ensure reliable delivery (not to be listed) +const vmime::word maildirUtils::CUR_DIR("cur"); // no longer new messages +const vmime::word maildirUtils::NEW_DIR("new"); // unread messages + + +const utility::file::path maildirUtils::getFolderFSPath + (maildirStore* store, const utility::path& folderPath, const FolderFSPathMode mode) +{ + // Root path + utility::file::path path(store->getFileSystemPath()); + const int count = (mode == FOLDER_PATH_CONTAINER ? folderPath.size() : folderPath.size() - 1); + + // Parent folders + for (int i = 0 ; i < count ; ++i) + { + utility::file::path::component comp(folderPath[i]); + + // TODO: may not work with all encodings... + comp.buffer() = "." + comp.buffer() + ".directory"; + + path /= comp; + } + + // Last component + if (folderPath.size() != 0 && + mode != FOLDER_PATH_CONTAINER) + { + path /= folderPath.last(); + + switch (mode) + { + case FOLDER_PATH_ROOT: break; // Nothing to do + case FOLDER_PATH_NEW: path /= NEW_DIR; break; + case FOLDER_PATH_CUR: path /= CUR_DIR; break; + case FOLDER_PATH_TMP: path /= TMP_DIR; break; + case FOLDER_PATH_CONTAINER: break; // Can't happen... + } + } + + return (path); +} + + +const bool maildirUtils::isSubfolderDirectory(const utility::file& file) +{ + // A directory which name does not start with '.' + // is listed as a sub-folder... + if (file.isDirectory() && + file.fullPath().last().buffer().size() >= 1 && + file.fullPath().last().buffer()[0] != '.') + { + return (true); + } + + return (false); +} + + +} // messaging +} // vmime diff --git a/src/messaging/maildirUtils.hpp b/src/messaging/maildirUtils.hpp new file mode 100644 index 00000000..44d36ad3 --- /dev/null +++ b/src/messaging/maildirUtils.hpp @@ -0,0 +1,72 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_MAILDIRUTILS_HPP_INCLUDED +#define VMIME_MESSAGING_MAILDIRUTILS_HPP_INCLUDED + + +#include "../utility/file.hpp" +#include "../utility/path.hpp" + + +namespace vmime { +namespace messaging { + + +class maildirStore; + + +class maildirUtils +{ +public: + + /** Mode for return value of getFolderFSPath(). */ + enum FolderFSPathMode + { + FOLDER_PATH_ROOT, /**< Root folder (eg. ~/Mail/MyFolder) */ + FOLDER_PATH_NEW, /**< Folder containing unread messages (eg. ~/Mail/MyFolder/new) */ + FOLDER_PATH_CUR, /**< Folder containing messages that have been seen (eg. ~/Mail/MyFolder/cur) */ + FOLDER_PATH_TMP, /**< Temporary folder used for reliable delivery (eg. ~/Mail/MyFolder/tmp) */ + FOLDER_PATH_CONTAINER /**< Container for sub-folders (eg. ~/Mail/.MyFolder.directory) */ + }; + + /** Return the path on the filesystem for the folder in specified store. + * + * @param store parent store + * @param folderPath path of the folder + * @param mode type of path to return (see FolderFSPathMode) + * @return filesystem path for the specified folder + */ + static const utility::file::path getFolderFSPath(maildirStore* store, const utility::path& folderPath, const FolderFSPathMode mode); + + static const bool isSubfolderDirectory(const utility::file& file); + +private: + + static const vmime::word TMP_DIR; + static const vmime::word CUR_DIR; + static const vmime::word NEW_DIR; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_MAILDIRUTILS_HPP_INCLUDED diff --git a/src/messaging/message.cpp b/src/messaging/message.cpp new file mode 100644 index 00000000..0bb1aaca --- /dev/null +++ b/src/messaging/message.cpp @@ -0,0 +1,46 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "message.hpp" + + +namespace vmime { +namespace messaging { + + +const part& part::operator[](const int x) const +{ + return (structure()[x]); +} + + +part& part::operator[](const int x) +{ + return (structure()[x]); +} + + +const int part::count() const +{ + return (structure().count()); +} + + +} // messaging +} // vmime diff --git a/src/messaging/message.hpp b/src/messaging/message.hpp new file mode 100644 index 00000000..c07c9ca3 --- /dev/null +++ b/src/messaging/message.hpp @@ -0,0 +1,280 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_MESSAGE_HPP_INCLUDED +#define VMIME_MESSAGING_MESSAGE_HPP_INCLUDED + + +#include "../header.hpp" +#include "progressionListener.hpp" +#include "../utility/stream.hpp" + + +namespace vmime { +namespace messaging { + + +/** A MIME part in a message. + */ + +class part +{ +protected: + + part() { } + part(const part&) { } + + virtual ~part() { } + +public: + + /** Return the structure of this part. + * + * @return structure of the part + */ + virtual const class structure& structure() const = 0; + + /** Return the structure of this part. + * + * @return structure of the part + */ + virtual class structure& structure() = 0; + + /** Return the header section for this part (you must fetch header + * before using this function: see message::fetchPartHeader). + * + * @return header section + */ + virtual const class header& header() const = 0; + + /** Return the media-type of the content in this part. + * + * @return content media type + */ + virtual const mediaType& type() const = 0; + + /** Return the size of this part. + * + * @return size of the part (in bytes) + */ + virtual const int size() const = 0; + + /** Return the part sequence number (index) + * + * @return part number + */ + virtual const int number() const = 0; // begin at 1 + + /** Return the sub-part at the specified position. + * This provide easy access to parts: + * Eg: "message->extract(message->structure()[3][1][2])". + * + * @param x index of the sub-part + * @return sub-part at position 'x' + */ + const part& operator[](const int x) const; + + /** Return the sub-part at the specified position. + * This provide easy access to parts: + * Eg: "message->extract(message->structure()[3][1][2])". + * + * @param x index of the sub-part + * @return sub-part at position 'x' + */ + part& operator[](const int x); + + /** Return the number of sub-parts in this part. + * + * @return number of sub-parts + */ + const int count() const; +}; + + +/** Structure of a MIME part/message. + */ + +class structure +{ +protected: + + structure() { } + structure(const structure&) { } + + virtual ~structure() { } + +public: + + /** Return the part at the specified position. + * + * @param x position + * @return part at position 'x' + */ + virtual const part& operator[](const int x) const = 0; + + /** Return the part at the specified position. + * + * @param x position + * @return part at position 'x' + */ + virtual part& operator[](const int x) = 0; + + /** Return the number of parts in this part. + * + * @return number of parts + */ + virtual const int count() const = 0; +}; + + +/** Abstract representation of a message in a store/transport service. + */ + +class message +{ +protected: + + message() { } + message(const message&) { } + +public: + + virtual ~message() { } + + /** The type for an unique message identifier. + */ + typedef string uid; + + /** Return the MIME structure of the message (must fetch before). + * + * @return MIME structure of the message + */ + virtual const class structure& structure() const = 0; + + /** Return the MIME structure of the message (must fetch before). + * + * @return MIME structure of the message + */ + virtual class structure& structure() = 0; + + /** Return a reference to the header fields of the message (must fetch before). + * + * @return header section of the message + */ + virtual const class header& header() const = 0; + + /** Return the sequence number of this message. This number is + * used to reference the message in the folder. + * + * @return sequence number of the message + */ + virtual const int number() const = 0; + + /** Return the unique identified of this message (must fetch before). + * + * @return UID of the message + */ + virtual const uid uniqueId() const = 0; + + /** Return the size of the message (must fetch before). + * + * @return size of the message (in bytes) + */ + virtual const int size() const = 0; + + /** Check whether this message has been expunged + * (ie: definitively deleted). + * + * @return true if the message is expunged, false otherwise + */ + virtual const bool isExpunged() const = 0; + + /** Possible flags for a message. + */ + enum Flags + { + FLAG_SEEN = (1 << 0), /**< Message has been seen. */ + FLAG_RECENT = (1 << 1), /**< Message has been recently received. */ + FLAG_DELETED = (1 << 2), /**< Message is marked for deletion. */ + FLAG_REPLIED = (1 << 3), /**< User replied to this message. */ + FLAG_MARKED = (1 << 4), /**< Used-defined flag. */ + + FLAG_UNDEFINED = 9999 /**< Used internally (this should not be returned + by the flags() function). */ + }; + + /** Methods for setting the flags. + */ + enum FlagsModes + { + FLAG_MODE_SET, /**< Set (replace) the flags. */ + FLAG_MODE_ADD, /**< Add the flags. */ + FLAG_MODE_REMOVE /**< Remove the flags. */ + }; + + /** Return the flags of this message. + * + * @return flags of the message + */ + virtual const int flags() const = 0; + + /** Set the flags of this message. + * + * @param flags set of flags (see Flags) + * @param mode indicate how to treat old and new flags (see FlagsModes) + */ + virtual void setFlags(const int flags, const int mode = FLAG_MODE_SET) = 0; + + /** Extract the whole message data (header + contents). + * + * WARNING: partial fetch might not be supported by the underlying protocol. + * + * @param os output stream in which to write message data + * @param progress progression listener, or NULL if not used + * @param start index of the first byte to retrieve (used for partial fetch) + * @param length number of bytes to retrieve (used for partial fetch) + */ + + virtual void extract(utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const = 0; + + /** Extract the specified (MIME) part of the message (header + contents). + * + * WARNING: partial fetch might not be supported by the underlying protocol. + * + * @param p part to extract + * @param os output stream in which to write part data + * @param progress progression listener, or NULL if not used + * @param start index of the first byte to retrieve (used for partial fetch) + * @param length number of bytes to retrieve (used for partial fetch) + */ + virtual void extractPart(const part& p, utility::outputStream& os, progressionListener* progress = NULL, const int start = 0, const int length = -1) const = 0; + + /** Fetch the MIME header for the specified part. + * + * @param p the part for which to fetch the header + */ + virtual void fetchPartHeader(part& p) = 0; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_MESSAGE_HPP_INCLUDED diff --git a/src/messaging/progressionListener.hpp b/src/messaging/progressionListener.hpp new file mode 100644 index 00000000..1e75d862 --- /dev/null +++ b/src/messaging/progressionListener.hpp @@ -0,0 +1,75 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_PROGRESSIONLISTENER_HPP_INCLUDED +#define VMIME_MESSAGING_PROGRESSIONLISTENER_HPP_INCLUDED + + +namespace vmime { +namespace messaging { + + +/** An interface to implement if you want to be notified + * of a progression status by some objects. + */ + +class progressionListener +{ +protected: + + virtual ~progressionListener() { } + +public: + + /** Allow the caller object to cancel the current operation. + * + * @warning WARNING: this is implementation-dependant: the underlying + * messaging protocol may not support this). + * + * @return true to cancel the operation, false otherwise + */ + virtual const bool cancel() const = 0; + + /** Called at the beginning of the operation. + * + * @param predictedTotal predicted amount of units (this has + * no concrete meaning: they are not bytes, nor percentage...) + */ + virtual void start(const int predictedTotal) = 0; + + /** Called during the operation (can be called several times). + * + * @param current current position + * @param currentTotal adjusted total amount of units + */ + virtual void progress(const int current, const int currentTotal) = 0; + + /** Called at the end of the operation. + * + * @param total final total amount of units + */ + virtual void stop(const int total) = 0; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_PROGRESSIONLISTENER_HPP_INCLUDED diff --git a/src/messaging/service.cpp b/src/messaging/service.cpp new file mode 100644 index 00000000..5a55fb4f --- /dev/null +++ b/src/messaging/service.cpp @@ -0,0 +1,44 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "service.hpp" + +#include "defaultAuthenticator.hpp" + + +namespace vmime { +namespace messaging { + + +service::service(class session& sess, const serviceInfos& infos, class authenticator* auth) + : m_deleteAuth(auth == NULL), m_session(sess), m_auth(auth ? auth : + new defaultAuthenticator(sess.properties(), infos.propertyPrefix())) +{ +} + + +service::~service() +{ + if (m_deleteAuth) + delete (m_auth); +} + + +} // messaging +} // vmime diff --git a/src/messaging/service.hpp b/src/messaging/service.hpp new file mode 100644 index 00000000..d4a59c7f --- /dev/null +++ b/src/messaging/service.hpp @@ -0,0 +1,143 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SERVICE_HPP_INCLUDED +#define VMIME_MESSAGING_SERVICE_HPP_INCLUDED + + +#include "../types.hpp" +#include "session.hpp" + +#include "authenticator.hpp" +#include "progressionListener.hpp" + +#include "serviceFactory.hpp" +#include "serviceInfos.hpp" + + +namespace vmime { +namespace messaging { + + +class service +{ +protected: + + service(class session& sess, const serviceInfos& infos, class authenticator* auth); + +public: + + virtual ~service(); + + // Possible service types + enum Type + { + TYPE_STORE = 0, /**< The service is a message store. */ + TYPE_TRANSPORT /**< The service sends messages. */ + }; + + /** Return the type of service. + * + * @return type of service + */ + virtual const Type type() const = 0; + + /** Return the protocol name of this service. + * + * @return protocol name + */ + virtual const string protocolName() const = 0; + + /** Return the session object associated with this service instance. + * + * @return session object + */ + const class session& session() const { return (m_session); } + + /** Return the session object associated with this service instance. + * + * @return session object + */ + class session& session() { return (m_session); } + + /** Return information about this service. + * + * @return information about the service + */ + virtual const serviceInfos& infos() const = 0; + + /** Connect to service. + */ + virtual void connect() = 0; + + /** Disconnect from service. + */ + virtual void disconnect() = 0; + + /** Test whether this service is connected. + * + * @return true if the service is connected, false otherwise + */ + virtual const bool isConnected() const = 0; + + /** Do nothing but ensure the server do not disconnect (for + * example, this can reset the auto-logout timer on the + * server, if one exists). + */ + virtual void noop() = 0; + + /** Return the authenticator object used with this service instance. + * + * @return authenticator object + */ + const class authenticator& authenticator() const { return (*m_auth); } + + /** Return the authenticator object used with this service instance. + * + * @return authenticator object + */ + class authenticator& authenticator() { return (*m_auth); } + + // Basic service registerer + template + class initializer + { + public: + + initializer(const string& protocol) + { + serviceFactory::getInstance()-> + template registerName (protocol); + } + }; + +private: + + bool m_deleteAuth; + + class session& m_session; + class authenticator* m_auth; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SERVICE_HPP_INCLUDED diff --git a/src/messaging/serviceFactory.cpp b/src/messaging/serviceFactory.cpp new file mode 100644 index 00000000..09bced01 --- /dev/null +++ b/src/messaging/serviceFactory.cpp @@ -0,0 +1,102 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "serviceFactory.hpp" +#include "service.hpp" + +#include "../exception.hpp" +#include "../config.hpp" + +#include "builtinServices.inl" + + +namespace vmime { +namespace messaging { + + +serviceFactory::serviceFactory() +{ +} + + +serviceFactory::~serviceFactory() +{ + for (ProtoMap::iterator it = m_protoMap.begin() ; it != m_protoMap.end() ; ++it) + delete ((*it).second); +} + + +service* serviceFactory::create + (session& sess, const string& protocol, authenticator* auth) +{ + ProtoMap::const_iterator pos = m_protoMap.find(toLower(protocol)); + + if (pos != m_protoMap.end()) + { + return ((*pos).second)->create(sess, auth); + } + else + { + throw exceptions::no_service_available(); + return (NULL); + } +} + + +service* serviceFactory::create + (session& sess, const url& u, authenticator* auth) +{ + service* serv = create(sess, u.protocol(), auth); + + sess.properties()[serv->infos().propertyPrefix() + "server.address"] = u.host(); + + if (u.port() != url::UNSPECIFIED_PORT) + sess.properties()[serv->infos().propertyPrefix() + "server.port"] = u.port(); + + if (!u.path().empty()) + sess.properties()[serv->infos().propertyPrefix() + "server.path"] = u.path(); + + if (!u.username().empty()) + { + sess.properties()[serv->infos().propertyPrefix() + "auth.username"] = u.username(); + sess.properties()[serv->infos().propertyPrefix() + "auth.password"] = u.password(); + } + + return (serv); +} + + +const serviceFactory::registeredService& serviceFactory::operator[] + (const string& protocol) const +{ + ProtoMap::const_iterator pos = m_protoMap.find(toLower(protocol)); + + if (pos != m_protoMap.end()) + { + return *((*pos).second); + } + else + { + throw exceptions::no_service_available(); + } +} + + +} // messaging +} // vmime diff --git a/src/messaging/serviceFactory.hpp b/src/messaging/serviceFactory.hpp new file mode 100644 index 00000000..9aa8e46a --- /dev/null +++ b/src/messaging/serviceFactory.hpp @@ -0,0 +1,207 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SERVICEFACTORY_HPP_INCLUDED +#define VMIME_MESSAGING_SERVICEFACTORY_HPP_INCLUDED + + +#include + +#include "../types.hpp" +#include "../base.hpp" +#include "../utility/singleton.hpp" + +#include "serviceInfos.hpp" +#include "authenticator.hpp" +#include "progressionListener.hpp" +#include "timeoutHandler.hpp" +#include "url.hpp" + + +namespace vmime { +namespace messaging { + + +class service; +class session; + + +/** A factory to create 'service' objects for a specified protocol. + */ + +class serviceFactory : public utility::singleton +{ + friend class utility::singleton ; + +protected: + + serviceFactory(); + ~serviceFactory(); + +public: + + class registeredService + { + friend class serviceFactory; + + protected: + + virtual ~registeredService() { } + + public: + + virtual service* create(session& sess, authenticator* auth) = 0; + + virtual const string& name() const = 0; + virtual const serviceInfos& infos() const = 0; + }; + +private: + + template + class registeredServiceImpl : public registeredService + { + friend class serviceFactory; + + protected: + + registeredServiceImpl(const string& name) + : m_name(name), m_servInfos(S::infosInstance()) + { + } + + public: + + service* create(session& sess, authenticator* auth) + { + return new S(sess, auth); + } + + const serviceInfos& infos() const + { + return (m_servInfos); + } + + const string& name() const + { + return (m_name); + } + + private: + + const string m_name; + const serviceInfos& m_servInfos; + }; + + typedef std::map ProtoMap; + ProtoMap m_protoMap; + +public: + + template + void registerName(const string& protocol) + { + const string name = vmime::toLower(protocol); + m_protoMap.insert(ProtoMap::value_type(name, + new registeredServiceImpl (name))); + } + + service* create(session& sess, const string& protocol, authenticator* auth = NULL); + service* create(session& sess, const url& u, authenticator* auth = NULL); + + const registeredService& operator[](const string& protocol) const; + + + class iterator; + + class const_iterator + { + friend class serviceFactory; + + public: + + const_iterator() { } + const_iterator(const const_iterator& it) : m_it(it.m_it) { } + const_iterator(const iterator& it) : m_it(it.m_it) { } + + const_iterator& operator=(const const_iterator& it) { m_it = it.m_it; return (*this); } + + const registeredService& operator*() const { return (*(*m_it).second); } + const registeredService* operator->() const { return ((*m_it).second); } + + const_iterator& operator++() { ++m_it; return (*this); } + const_iterator operator++(int) { return (m_it++); } + + const_iterator& operator--() { --m_it; return (*this); } + const_iterator operator--(int) { return (m_it--); } + + const bool operator==(const const_iterator& it) const { return (m_it == it.m_it); } + const bool operator!=(const const_iterator& it) const { return (m_it != it.m_it); } + + private: + + const_iterator(const ProtoMap::const_iterator it) : m_it(it) { } + + ProtoMap::const_iterator m_it; + }; + + class iterator + { + friend class serviceFactory; + friend class serviceFactory::const_iterator; + + public: + + iterator() { } + iterator(const iterator& it) : m_it(it.m_it) { } + + iterator& operator=(const iterator& it) { m_it = it.m_it; return (*this); } + + registeredService& operator*() const { return (*(*m_it).second); } + registeredService* operator->() const { return ((*m_it).second); } + + iterator& operator++() { ++m_it; return (*this); } + iterator operator++(int) { return (m_it++); } + + iterator& operator--() { --m_it; return (*this); } + iterator operator--(int) { return (m_it--); } + + const bool operator==(const iterator& it) const { return (m_it == it.m_it); } + const bool operator!=(const iterator& it) const { return (m_it != it.m_it); } + + private: + + iterator(const ProtoMap::iterator it) : m_it(it) { } + + ProtoMap::iterator m_it; + }; + + iterator begin() { return iterator(m_protoMap.begin()); } + iterator end() { return iterator(m_protoMap.end()); } + + const_iterator begin() const { return const_iterator(m_protoMap.begin()); } + const_iterator end() const { return const_iterator(m_protoMap.end()); } +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SERVICEFACTORY_HPP_INCLUDED diff --git a/src/messaging/serviceInfos.hpp b/src/messaging/serviceInfos.hpp new file mode 100644 index 00000000..9da5ab26 --- /dev/null +++ b/src/messaging/serviceInfos.hpp @@ -0,0 +1,75 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SERVICEINFOS_HPP_INCLUDED +#define VMIME_MESSAGING_SERVICEINFOS_HPP_INCLUDED + + +#include + +#include "../types.hpp" + + +namespace vmime { +namespace messaging { + + +class serviceInfos +{ + friend class serviceFactory; + +protected: + + serviceInfos() { } + serviceInfos(const serviceInfos&) { } + +private: + + serviceInfos& operator=(const serviceInfos&) { return (*this); } + +public: + + virtual ~serviceInfos() { } + + /** Return the default port used for the underlying protocol. + * + * @return default port number + */ + virtual const port_t defaultPort() const = 0; + + /** Return the property prefix used by this service. + * Use this to set/get properties in the session object. + * + * @return property prefix + */ + virtual const string propertyPrefix() const = 0; + + /** Return a list of available properties for this service. + * + * @return list of property names + */ + virtual const std::vector availableProperties() const = 0; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SERVICEINFOS_HPP_INCLUDED diff --git a/src/messaging/session.cpp b/src/messaging/session.cpp new file mode 100644 index 00000000..be1d8206 --- /dev/null +++ b/src/messaging/session.cpp @@ -0,0 +1,88 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "session.hpp" +#include "serviceFactory.hpp" + +#include "store.hpp" +#include "transport.hpp" + + +namespace vmime { +namespace messaging { + + +session::session() +{ +} + + +session::session(const propertySet& props) + : m_props(props) +{ +} + + +session::~session() +{ +} + + +transport* session::getTransport(authenticator* auth) +{ + return (getTransport(m_props["transport.protocol"], auth)); +} + + +transport* session::getTransport(const string& protocol, authenticator* auth) +{ + service* sv = serviceFactory::getInstance()->create(*this, protocol, auth); + + if (sv->type() != service::TYPE_TRANSPORT) + { + delete (sv); + throw exceptions::no_service_available(); + } + + return static_cast(sv); +} + + +store* session::getStore(authenticator* auth) +{ + return (getStore(m_props["store.protocol"], auth)); +} + + +store* session::getStore(const string& protocol, authenticator* auth) +{ + service* sv = serviceFactory::getInstance()->create(*this, protocol, auth); + + if (sv->type() != service::TYPE_STORE) + { + delete (sv); + throw exceptions::no_service_available(); + } + + return static_cast(sv); +} + + +} // messaging +} // vmime diff --git a/src/messaging/session.hpp b/src/messaging/session.hpp new file mode 100644 index 00000000..43bbe0b1 --- /dev/null +++ b/src/messaging/session.hpp @@ -0,0 +1,113 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SESSION_HPP_INCLUDED +#define VMIME_MESSAGING_SESSION_HPP_INCLUDED + + +#include "authenticator.hpp" +#include "progressionListener.hpp" + +#include "../propertySet.hpp" + + +namespace vmime { +namespace messaging { + + +class store; +class transport; + + +/** An object that contains all the information needed + * for connection to a service. + */ + +class session +{ +public: + + session(); + session(const propertySet& props); + + virtual ~session(); + + /** Return a transport service instance for the protocol specified + * in the session properties. + * + * The property "transport.protocol" specify the protocol to use. + * + * @param auth authenticator object to use for the new transport service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new transport service + */ + transport* getTransport(authenticator* auth = NULL); + + /** Return a transport service instance for the specified protocol. + * + * @param protocol transport protocol to use (eg. "smtp") + * @param auth authenticator object to use for the new transport service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new transport service + */ + transport* getTransport(const string& protocol, authenticator* auth = NULL); + + /** Return a transport service instance for the protocol specified + * in the session properties. + * + * The property "store.protocol" specify the protocol to use. + * + * @param auth authenticator object to use for the new store service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new transport service + */ + store* getStore(authenticator* auth = NULL); + + /** Return a store service instance for the specified protocol. + * + * @param protocol store protocol to use (eg. "imap") + * @param auth authenticator object to use for the new store service. If + * NULL, a default one is used. The default authenticator simply return user + * credentials by reading the session properties "auth.username" and "auth.password". + * @return a new transport service + */ + store* getStore(const string& protocol, authenticator* auth = NULL); + + /** Properties for the session and for the services. + */ + const propertySet& properties() const { return (m_props); } + + /** Properties for the session and for the services. + */ + propertySet& properties() { return (m_props); } + +private: + + propertySet m_props; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SESSION_HPP_INCLUDED diff --git a/src/messaging/simpleAuthenticator.cpp b/src/messaging/simpleAuthenticator.cpp new file mode 100644 index 00000000..557efd63 --- /dev/null +++ b/src/messaging/simpleAuthenticator.cpp @@ -0,0 +1,45 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "simpleAuthenticator.hpp" + + +namespace vmime { +namespace messaging { + + +simpleAuthenticator::simpleAuthenticator() +{ +} + + +simpleAuthenticator::simpleAuthenticator(const string& username, const string& password) + : m_username(username), m_password(password) +{ +} + + +const authenticationInfos simpleAuthenticator::getAuthInfos() const +{ + return (authenticationInfos(m_username, m_password)); +} + + +} // messaging +} // vmime diff --git a/src/messaging/simpleAuthenticator.hpp b/src/messaging/simpleAuthenticator.hpp new file mode 100644 index 00000000..9d19f4c6 --- /dev/null +++ b/src/messaging/simpleAuthenticator.hpp @@ -0,0 +1,59 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SIMPLEAUTHENTICATOR_HPP_INCLUDED +#define VMIME_MESSAGING_SIMPLEAUTHENTICATOR_HPP_INCLUDED + + +#include "authenticator.hpp" + + +namespace vmime { +namespace messaging { + + +class simpleAuthenticator : public authenticator +{ +public: + + simpleAuthenticator(); + simpleAuthenticator(const string& username, const string& password); + +public: + + const string& username() const { return (m_username); } + string& username() { return (m_username); } + + const string& password() const { return (m_password); } + string& password() { return (m_password); } + +private: + + string m_username; + string m_password; + + const authenticationInfos getAuthInfos() const; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SIMPLEAUTHENTICATOR_HPP_INCLUDED diff --git a/src/messaging/socket.hpp b/src/messaging/socket.hpp new file mode 100644 index 00000000..4cf69536 --- /dev/null +++ b/src/messaging/socket.hpp @@ -0,0 +1,98 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_SOCKET_HPP_INCLUDED +#define VMIME_MESSAGING_SOCKET_HPP_INCLUDED + + +#include "../base.hpp" + + +namespace vmime { +namespace messaging { + + +class socket +{ +public: + + virtual ~socket() { } + + /** Connect to the specified address and port. + * + * @param address server address (this can be a full qualified domain name + * or an IP address, doesn't matter) + * @param port server port + */ + virtual void connect(const string& address, const port_t port) = 0; + + /** Disconnect from the server. + */ + virtual void disconnect() = 0; + + /** Test whether this socket is connected. + * + * @return true if the socket is connected, false otherwise + */ + virtual const bool isConnected() const = 0; + + /** Receive (text) data from the socket. + * + * @param buffer buffer in which to write received data + */ + virtual void receive(string& buffer) = 0; + + /** Receive (raw) data from the socket. + * + * @param buffer buffer in which to write received data + * @param count maximum number of bytes to receive (size of buffer) + * @return number of bytes received/written into output buffer + */ + virtual const int receiveRaw(char* buffer, const int count) = 0; + + /** Send (text) data to the socket. + * + * @param buffer data to send + */ + virtual void send(const string& buffer) = 0; + + /** Send (raw) data to the socket. + * + * @param buffer data to send + * @param count number of bytes to send (size of buffer) + */ + virtual void sendRaw(const char* buffer, const int count) = 0; +}; + + +class socketFactory +{ +public: + + virtual ~socketFactory() { } + + virtual socket* create() = 0; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_SOCKET_HPP_INCLUDED diff --git a/src/messaging/store.hpp b/src/messaging/store.hpp new file mode 100644 index 00000000..687975d3 --- /dev/null +++ b/src/messaging/store.hpp @@ -0,0 +1,75 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_STORE_HPP_INCLUDED +#define VMIME_MESSAGING_STORE_HPP_INCLUDED + + +#include "service.hpp" +#include "folder.hpp" + + +namespace vmime { +namespace messaging { + + +/** A store service. + * Encapsulate protocols that provide access to user's mail drop. + */ + +class store : public service +{ +protected: + + store(class session& sess, const serviceInfos& infos, class authenticator* auth) + : service(sess, infos, auth) { } + +public: + + /** Return the default folder. This is protocol dependant + * and usually is the INBOX folder. + * + * @return default folder + */ + virtual folder* getDefaultFolder() = 0; + + /** Return the root folder. This is protocol dependant + * and usually is the user's mail drop root folder + * + * @return root folder + */ + virtual folder* getRootFolder() = 0; + + /** Return the folder specified by the path. + * + * @param path absolute folder path + * @return folder at the specified path + */ + virtual folder* getFolder(const folder::path& path) = 0; + + + const Type type() const { return (TYPE_STORE); } +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_STORE_HPP_INCLUDED diff --git a/src/messaging/timeoutHandler.hpp b/src/messaging/timeoutHandler.hpp new file mode 100644 index 00000000..a7675b79 --- /dev/null +++ b/src/messaging/timeoutHandler.hpp @@ -0,0 +1,71 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_TIMEOUTHANDLER_HPP_INCLUDED +#define VMIME_MESSAGING_TIMEOUTHANDLER_HPP_INCLUDED + + +namespace vmime { +namespace messaging { + + +/** A class to manage time-out in messaging services. + */ + +class timeoutHandler +{ +public: + + virtual ~timeoutHandler() { } + + /** Called to test if the time limit has been reached. + * + * @return true if the time-out delay is elapsed + */ + virtual const bool isTimeOut() = 0; + + /** Called to reset the time-out counter. + */ + virtual void resetTimeOut() = 0; + + /** Called when the time limit has been reached (when + * isTimeOut() returned true). + * + * @return true to continue (and reset the time-out) + * or false to cancel the current operation + */ + virtual const bool handleTimeOut() = 0; +}; + + +class timeoutHandlerFactory +{ +public: + + virtual ~timeoutHandlerFactory() { } + + virtual timeoutHandler* create() = 0; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_TIMEOUTHANDLER_HPP_INCLUDED diff --git a/src/messaging/transport.hpp b/src/messaging/transport.hpp new file mode 100644 index 00000000..588eb24b --- /dev/null +++ b/src/messaging/transport.hpp @@ -0,0 +1,76 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_TRANSPORT_HPP_INCLUDED +#define VMIME_MESSAGING_TRANSPORT_HPP_INCLUDED + + +#include "service.hpp" +#include "../utility/stream.hpp" + + +namespace vmime { + +class message; +class mailbox; +class mailboxList; + +namespace messaging { + + +/** A transport service. + * Encapsulate protocols that can send messages. + */ + +class transport : public service +{ +protected: + + transport(class session& sess, const serviceInfos& infos, class authenticator* auth) + : service(sess, infos, auth) { } + +public: + + /** Send a message over this transport service. + * + * @param msg message to send + * @param progress progression listener, or NULL if not used + */ + virtual void send(vmime::message* msg, progressionListener* progress = NULL) = 0; + + /** Send a message over this transport service. + * + * @param expeditor expeditor mailbox + * @param recipients list of recipient mailboxes + * @param is input stream provding message data (header + body) + * @param size size of the message data + * @param progress progression listener, or NULL if not used + */ + virtual void send(const mailbox& expeditor, const mailboxList& recipients, utility::inputStream& is, const utility::stream::size_type size, progressionListener* progress = NULL) = 0; + + + const Type type() const { return (TYPE_TRANSPORT); } +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_TRANSPORT_HPP_INCLUDED diff --git a/src/messaging/url.cpp b/src/messaging/url.cpp new file mode 100644 index 00000000..cd38c656 --- /dev/null +++ b/src/messaging/url.cpp @@ -0,0 +1,226 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "url.hpp" + +#include "parserHelpers.hpp" +#include "urlUtils.hpp" +#include "exception.hpp" + +#include + + +namespace vmime { +namespace messaging { + + +// Known protocols +const string url::PROTOCOL_FILE = "file"; +const string url::PROTOCOL_HTTP = "http"; +const string url::PROTOCOL_FTP = "ftp"; + + + +url::url(const string& s) +{ + parse(s); +} + + +url::url(const url& u) +{ + operator=(u); +} + + +url::url(const string& protocol, const string& host, const port_t port, + const string& path, const string& username, const string& password) + : m_protocol(protocol), m_username(username), m_password(password), + m_host(host), m_port(port), m_path(path) +{ +} + + +url& url::operator=(const url& u) +{ + m_protocol = u.m_protocol; + + m_username = u.m_username; + m_password = u.m_password; + + m_host = u.m_host; + m_port = u.m_port; + + m_path = u.m_path; + + return (*this); +} + + +url& url::operator=(const string& s) +{ + parse(s); + + return (*this); +} + + +url::operator string() const +{ + return build(); +} + + +const string url::build() const +{ + std::ostringstream oss; + + oss << m_protocol << "://"; + + if (!m_username.empty()) + { + oss << urlUtils::encode(m_username); + + if (!m_password.empty()) + { + oss << ":"; + oss << urlUtils::encode(m_password); + } + + oss << "@"; + } + + oss << urlUtils::encode(m_host); + + if (m_port != UNSPECIFIED_PORT) + { + oss << ":"; + oss << m_port; + } + + if (!m_path.empty()) + { + oss << "/"; + oss << urlUtils::encode(m_path); + } + + return (oss.str()); +} + + +void url::parse(const string& str) +{ + // Protocol + const string::size_type protoEnd = str.find("://"); + if (protoEnd == string::npos) throw exceptions::malformed_url("No protocol separator"); + + const string proto = + toLower(string(str.begin(), str.begin() + protoEnd)); + + // Username/password + string::size_type slashPos = str.find('/', protoEnd + 3); + if (slashPos == string::npos) slashPos = str.length(); + + string::size_type atPos = str.find('@', protoEnd + 3); + string hostPart; + + string username; + string password; + + if (atPos != string::npos && atPos < slashPos) + { + const string userPart(str.begin() + protoEnd, str.begin() + atPos); + const string::size_type colonPos = userPart.find(':'); + + if (colonPos == string::npos) + { + username = userPart; + } + else + { + username = string(userPart.begin(), userPart.begin() + colonPos); + password = string(userPart.begin() + colonPos + 1, userPart.end()); + } + + hostPart = string(str.begin() + atPos + 1, str.begin() + slashPos); + } + else + { + hostPart = string(str.begin() + protoEnd, str.begin() + slashPos); + } + + // Host/port + const string::size_type colonPos = hostPart.find(':'); + + string host; + string port; + + if (colonPos == string::npos) + { + host = hostPart; + } + else + { + host = string(hostPart.begin(), hostPart.begin() + colonPos); + port = string(hostPart.begin() + colonPos + 1, hostPart.end()); + } + + // Path + string path(str.begin() + slashPos, str.end()); + + if (path == "/") + path.clear(); + + // Some sanity check + if (proto.empty()) + throw exceptions::malformed_url("No protocol specified"); + else if (host.empty() && path.empty()) // Accept empty host (eg. "file:///home/vincent/mydoc") + throw exceptions::malformed_url("No host specified"); + + bool onlyDigit = true; + + for (string::const_iterator it = port.begin() ; + onlyDigit && it != port.end() ; ++it) + { + onlyDigit = isdigit(*it); + } + + if (!onlyDigit) + throw exceptions::malformed_url("Port can only contain digits"); + + std::istringstream iss(port); + port_t portNum = 0; + + iss >> portNum; + + // Now, save URL parts + m_protocol = proto; + + m_username = urlUtils::decode(username); + m_password = urlUtils::decode(password); + + m_host = urlUtils::decode(host); + m_port = portNum; + + m_path = urlUtils::decode(path); +} + + +} // messaging +} // vmime diff --git a/src/messaging/url.hpp b/src/messaging/url.hpp new file mode 100644 index 00000000..8cef2d16 --- /dev/null +++ b/src/messaging/url.hpp @@ -0,0 +1,125 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_URL_HPP_INCLUDED +#define VMIME_MESSAGING_URL_HPP_INCLUDED + + +#include "../types.hpp" +#include "../base.hpp" + + +namespace vmime { +namespace messaging { + + +/** This class represents a Uniform Resource Locator (a pointer + * to a "resource" on the World Wide Web). + */ + +class url +{ +public: + + /** Means "port not specified" (use default port). + */ + static const port_t UNSPECIFIED_PORT = static_cast (-1); + + static const string PROTOCOL_FILE; + static const string PROTOCOL_HTTP; + static const string PROTOCOL_FTP; + + + /** Construct an URL from a string (parse the URL components). + * + * @param s full URL string (eg. http://vmime.sourceforge.net:80/download.html + */ + url(const string& s); + + /** Construct an URL from another URL object. + * + * @param u other URL object + */ + url(const url& u); + + /** Construct an URL from the components. + * + * @param protocol protocol (eg. "http", "ftp"...) + * @param host host name (eg. "vmime.sourceforge.net", "123.45.67.89") + * @param port optional port number (eg. 80, 110 or UNSPECIFIED_PORT to mean "default") + * @param path optional full path (eg. "download.html") + * @param username optional user name + * @param password optional user password + */ + url(const string& protocol, const string& host, const port_t port = UNSPECIFIED_PORT, + const string& path = "", const string& username = "", const string& password = ""); + + + const string& protocol() const { return (m_protocol); } + string& protocol() { return (m_protocol); } + + const string& username() const { return (m_username); } + string& username() { return (m_username); } + + const string& password() const { return (m_password); } + string& password() { return (m_password); } + + const string& host() const { return (m_host); } + string& host() { return (m_host); } + + const port_t port() const { return (m_port); } + port_t& port() { return (m_port); } + + const string& path() const { return (m_path); } + string& path() { return (m_path); } + + + /** Build a string URL from this object. + */ + operator string() const; + + url& operator=(const url& u); + url& operator=(const string& s); + +private: + + const string build() const; + void parse(const string& str); + + // Format: + // "protocol://[username[:password]@]host[:port][/path]" + + string m_protocol; + + string m_username; + string m_password; + + string m_host; + + port_t m_port; + + string m_path; +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_URL_HPP_INCLUDED diff --git a/src/messaging/urlUtils.cpp b/src/messaging/urlUtils.cpp new file mode 100644 index 00000000..b8e9cb91 --- /dev/null +++ b/src/messaging/urlUtils.cpp @@ -0,0 +1,93 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "urlUtils.hpp" + + +namespace vmime { +namespace messaging { + + +const string urlUtils::encode(const string& s) +{ + string result; + result.reserve(s.length()); + + for (string::const_iterator it = s.begin() ; it != s.end() ; ++it) + { + const char_t c = *it; + + if (isprint(c) && c != '%') + { + result += c; + } + else + { + char hex[4]; + + hex[0] = '%'; + hex[1] = c / 16; + hex[2] = c % 16; + hex[3] = 0; + + result += hex; + } + } + + return (result); +} + + +const string urlUtils::decode(const string& s) +{ + string result; + result.reserve(s.length()); + + for (string::const_iterator it = s.begin() ; it != s.end() ; ) + { + const char_t c = *it; + + switch (c) + { + case '%': + { + const char_t p = (++it != s.end() ? *it : 0); + const char_t q = (++it != s.end() ? *it : 0); + + result += static_cast (p * 16 + q); + + if (it != s.end()) + ++it; + + break; + } + default: + + result += c; + ++it; + break; + } + } + + return (result); +} + + +} // messaging +} // vmime diff --git a/src/messaging/urlUtils.hpp b/src/messaging/urlUtils.hpp new file mode 100644 index 00000000..135d508e --- /dev/null +++ b/src/messaging/urlUtils.hpp @@ -0,0 +1,51 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_MESSAGING_URLUTILS_HPP_INCLUDED +#define VMIME_MESSAGING_URLUTILS_HPP_INCLUDED + + +#include "../types.hpp" +#include "../base.hpp" + + +namespace vmime { +namespace messaging { + + +class urlUtils +{ +public: + + /** Encode extended characters in a URL string (ASCII characters + * are unmodified, other are encoded as '%' followed by hex code). + */ + static const string encode(const string& s); + + /** Decode an hex-encoded URL (see encode()). + */ + static const string decode(const string& s); +}; + + +} // messaging +} // vmime + + +#endif // VMIME_MESSAGING_URLUTILS_HPP_INCLUDED diff --git a/src/options.cpp b/src/options.cpp new file mode 100644 index 00000000..cd630a1d --- /dev/null +++ b/src/options.cpp @@ -0,0 +1,27 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "options.hpp" + + +namespace vmime +{ + + +} // vmime diff --git a/src/options.hpp b/src/options.hpp new file mode 100644 index 00000000..bfc2f243 --- /dev/null +++ b/src/options.hpp @@ -0,0 +1,98 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_OPTIONS_HPP_INCLUDED +#define VMIME_OPTIONS_HPP_INCLUDED + + +#include "base.hpp" +#include "utility/singleton.hpp" + + +namespace vmime +{ + + +/** A class to set global options for VMime. + */ + +class options : public utility::singleton +{ + friend class utility::singleton ; + +protected: + + /** Message-related options. + */ + class messageOptions + { + protected: + + friend class options; + + messageOptions() + : m_maxLineLength(lineLengthLimits::convenient) + { + } + + string::size_type m_maxLineLength; + + public: + + const string::size_type& maxLineLength() const { return (m_maxLineLength); } + string::size_type& maxLineLength() { return (m_maxLineLength); } + }; + + /** Multipart-related options. + */ + class multipartOptions + { + protected: + + friend class options; + + multipartOptions() + : m_prologText("This is a multi-part message in MIME format. Your mail reader does not understand MIME message format."), + m_epilogText("") + { + } + + string m_prologText; + string m_epilogText; + + public: + + const string& prologText() const { return (m_prologText); } + string& prologText() { return (m_prologText); } + + const string& epilogText() const { return (m_epilogText); } + string& epilogText() { return (m_epilogText); } + }; + +public: + + multipartOptions multipart; + messageOptions message; +}; + + +} // vmime + + +#endif // VMIME_OPTIONS_HPP_INCLUDED diff --git a/src/parameter.cpp b/src/parameter.cpp new file mode 100644 index 00000000..98111cb0 --- /dev/null +++ b/src/parameter.cpp @@ -0,0 +1,43 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "parameter.hpp" +#include "parameterFactory.hpp" + + +namespace vmime +{ + + +parameter* parameter::clone() const +{ + parameter* p = parameterFactory::getInstance()->create(m_name); + p->copyFrom(*this); + + return (p); +} + + +void parameter::copyFrom(const parameter& param) +{ + m_name = param.m_name; +} + + +} // vmime diff --git a/src/parameter.hpp b/src/parameter.hpp new file mode 100644 index 00000000..57f130ba --- /dev/null +++ b/src/parameter.hpp @@ -0,0 +1,55 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PARAMETER_HPP_INCLUDED +#define VMIME_PARAMETER_HPP_INCLUDED + + +#include "component.hpp" + + +namespace vmime +{ + + +class parameter : public component +{ + friend class parameterFactory; // for "parse()" + +public: + + parameter* clone() const; + +protected: + + string m_name; + +public: + + virtual void copyFrom(const parameter& param); + + const string& name() const { return (m_name); } + string& name() { return (m_name); } +}; + + +} // vmime + + +#endif // VMIME_PARAMETER_HPP_INCLUDED diff --git a/src/parameterFactory.cpp b/src/parameterFactory.cpp new file mode 100644 index 00000000..e67a8ee3 --- /dev/null +++ b/src/parameterFactory.cpp @@ -0,0 +1,71 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "parameterFactory.hpp" +#include "exception.hpp" + +#include "textParameter.hpp" +#include "charsetParameter.hpp" +#include "dateParameter.hpp" + + +namespace vmime +{ + + +parameterFactory::parameterFactory() +{ + // Register some default names + registerName ("charset"); + registerName ("creation-date"); + registerName ("modification-date"); + registerName ("read-date"); +} + + +parameterFactory::~parameterFactory() +{ +} + + +parameter* parameterFactory::create + (const string& name, const string& value) +{ + const string _name = toLower(name); + + NameMap::const_iterator pos = m_nameMap.find(_name); + parameter* param = NULL; + + if (pos != m_nameMap.end()) + { + param = ((*pos).second)(); + } + else + { + param = new textParameter; + } + + param->name() = _name; + if (value != NULL_STRING) param->parse(value); + + return (param); +} + + +} // vmime diff --git a/src/parameterFactory.hpp b/src/parameterFactory.hpp new file mode 100644 index 00000000..8bff7e33 --- /dev/null +++ b/src/parameterFactory.hpp @@ -0,0 +1,73 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PARAMETERFACTORY_HPP_INCLUDED +#define VMIME_PARAMETERFACTORY_HPP_INCLUDED + + +#include "parameter.hpp" +#include "utility/singleton.hpp" + + +namespace vmime +{ + + +class parameterFactory : public utility::singleton +{ + friend class utility::singleton ; + +protected: + + parameterFactory(); + ~parameterFactory(); + + typedef parameter* (*AllocFunc)(void); + typedef std::map NameMap; + + NameMap m_nameMap; + + template + class registerer + { + public: + + static parameter* creator() + { + // Allocate a new object + return new TYPE(); + } + }; + +public: + + template + void registerName(const string& name) + { + m_nameMap.insert(NameMap::value_type(toLower(name), ®isterer::creator)); + } + + parameter* create(const string& name, const string& value = NULL_STRING); +}; + + +} // vmime + + +#endif // VMIME_PARAMETERFACTORY_HPP_INCLUDED diff --git a/src/parameterizedHeaderField.cpp b/src/parameterizedHeaderField.cpp new file mode 100644 index 00000000..ee00cd32 --- /dev/null +++ b/src/parameterizedHeaderField.cpp @@ -0,0 +1,335 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "parameterizedHeaderField.hpp" +#include "parameterFactory.hpp" +#include "text.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +parameterizedHeaderField::parameterizedHeaderField() +{ +} + + +/* + This class handles field contents of the following form: + Field: VALUE; PARAM1="VALUE1"; PARAM2="VALUE2"... + + eg. RFC-1521 + + content := "Content-Type" ":" type "/" subtype *(";" parameter) + + parameter := attribute "=" value + + attribute := token ; case-insensitive + + value := token / quoted-string + + token := 1* + + tspecials := "(" / ")" / "<" / ">" / "@" + / "," / ";" / ":" / "\" / <"> + / "/" / "[" / "]" / "?" / "=" + ; Must be in quoted-string, + ; to use within parameter values +*/ + +void parameterizedHeaderField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* const pstart = buffer.data() + position; + const string::value_type* p = pstart; + + const string::size_type start = position; + + while (p < pend && *p != ';') ++p; + + parseValue(buffer, start, position + (p - pstart)); + + // If there is one or more parameters following... + if (p < pend) + { + while (*p == ';') + { + // Skip ';' + ++p; + + while (p < pend && isspace(*p)) ++p; + + const string::size_type attrStart = position + (p - pstart); + + while (p < pend && !(*p == ';' || *p == '=')) + ++p; + + if (p >= pend || *p == ';') + { + // Hmmmm... we didn't found an '=' sign. + // This parameter may not be valid so try to advance + // to the next one, if there is one. + while (p < pend && *p != ';') + ++p; + } + else + { + // Extract the attribute name + string::size_type attrEnd = position + (p - pstart); + + while (attrEnd != attrStart && isspace(buffer[attrEnd - 1])) + --attrEnd; + + // Skip '=' + ++p; + + // Skip white-spaces between '=' and the value + while (p < pend && isspace(*p)) ++p; + + // Extract the value + string value; + + // -- this is a quoted-string + if (*p == '"') + { + // Skip '"' + ++p; + + // Extract quoted-string + bool escape = false; + bool stop = false; + + std::ostringstream ss; + string::size_type start = position + (p - pstart); + + for ( ; p < pend && !stop ; ++p) + { + if (escape) + { + escape = false; + start = position + (p - pstart); + } + else + { + switch (*p) + { + case '"': + { + ss << string(buffer.begin() + start, + buffer.begin() + position + (p - pstart)); + + stop = true; + break; + } + case '\\': + { + ss << string(buffer.begin() + start, + buffer.begin() + position + (p - pstart)); + + escape = true; + break; + } + + } + } + } + + if (!stop) + { + ss << string(buffer.begin() + start, + buffer.begin() + position + (p - pstart)); + } + + value = ss.str(); + } + // -- the value is a simple token + else + { + const string::size_type valStart = position + (p - pstart); + + while (p < pend && *p != ';') + ++p; + + string::size_type valEnd = position + (p - pstart); + + while (valEnd != valStart && isspace(buffer[valEnd - 1])) + --valEnd; + + value = string(buffer.begin() + valStart, + buffer.begin() + valEnd); + } + + // Don't allow ill-formed parameters + if (attrStart != attrEnd && value.length()) + { + // Append this parameter to the list + parameters.m_params.push_back(parameterFactory::getInstance()-> + create(string(buffer.begin() + attrStart, + buffer.begin() + attrEnd), value)); + } + + // Skip white-spaces after this parameter + while (p < pend && isspace(*p)) ++p; + } + } + } + + if (newPosition) + *newPosition = end; +} + + +void parameterizedHeaderField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + // Parent header field + headerField::generate(os, maxLineLength, pos, &pos); + + // Value + const string value = generateValue(); + + encodeAndFoldText(os, text(value), maxLineLength - 1, + pos, &pos, encodeAndFoldFlags::none); + + // Parameters + for (std::vector ::const_iterator + it = parameters.m_params.begin() ; it != parameters.m_params.end() ; ++it) + { + const parameter& param = **it; + + os << "; "; + pos += 2; + + param.generate(os, maxLineLength, pos, &pos); + } + + if (newLinePos) + *newLinePos = pos; +} + + +void parameterizedHeaderField::copyFrom(const headerField& field) +{ + const parameterizedHeaderField& source = dynamic_cast(field); + + parameters.clear(); + + for (std::vector ::const_iterator i = source.parameters.m_params.begin() ; + i != source.parameters.m_params.end() ; ++i) + { + parameters.m_params.push_back((*i)->clone()); + } + + headerField::copyFrom(field); +} + + + +////////////////////// +// Params container // +////////////////////// + + +parameterizedHeaderField::paramsContainer::~paramsContainer() +{ + clear(); +} + + +parameter& parameterizedHeaderField::paramsContainer::find(const string& name) const +{ + const string _name = toLower(name); + + std::vector ::const_iterator pos = m_params.begin(); + const std::vector ::const_iterator end = m_params.end(); + + for ( ; pos != end && (*pos)->name() != _name ; ++pos); + + // No parameter with this name can be found + if (pos == end) + { + throw exceptions::no_such_parameter(name); + } + // Else, return a reference to the existing parameter + else + { + return (**pos); + } +} + + +parameter& parameterizedHeaderField::paramsContainer::get(const string& name) +{ + const string _name = toLower(name); + + std::vector ::iterator pos = m_params.begin(); + const std::vector ::iterator end = m_params.end(); + + for ( ; pos != end && (*pos)->name() != _name ; ++pos); + + // If no parameter with this name can be found, create a new one + if (pos == end) + { + parameter* param = parameterFactory::getInstance()->create(_name); + m_params.push_back(param); + + // Return a reference to the new parameter + return (*param); + } + // Else, return a reference to the existing parameter + else + { + return (**pos); + } +} + + +// Parameter insertion +void parameterizedHeaderField::paramsContainer::append(const parameter& param) +{ + m_params.push_back(param.clone()); +} + + +void parameterizedHeaderField::paramsContainer::insert(const iterator it, const parameter& param) +{ + m_params.insert(it.m_iterator, param.clone()); +} + + +// Parameter removing +void parameterizedHeaderField::paramsContainer::remove(const iterator it) +{ + delete (*it.m_iterator); + m_params.erase(it.m_iterator); +} + + +void parameterizedHeaderField::paramsContainer::clear() +{ + free_container(m_params); +} + + +} // vmime diff --git a/src/parameterizedHeaderField.hpp b/src/parameterizedHeaderField.hpp new file mode 100644 index 00000000..949aceb4 --- /dev/null +++ b/src/parameterizedHeaderField.hpp @@ -0,0 +1,201 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PARAMETERIZEDHEADERFIELD_HPP_INCLUDED +#define VMIME_PARAMETERIZEDHEADERFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "headerFieldFactory.hpp" +#include "parameter.hpp" +#include "exception.hpp" +#include "parameterFactory.hpp" + + +namespace vmime +{ + + +class parameterizedHeaderField : public headerField +{ + friend class headerFieldFactory::registerer ; + +protected: + + parameterizedHeaderField(); + +public: + + void copyFrom(const headerField& field); + + // A sub-class for parameter manipulation + class paramsContainer + { + friend class parameterizedHeaderField; + + protected: + + ~paramsContainer(); + + public: + + // Find the first parameter with the specified name. If no parameter + // is found, an exception is thrown. + parameter& find(const string& name) const; + + // Find the first parameter with the specified name + parameter& get(const string& name); + + // Parameter iterator + class const_iterator; + + class iterator + { + friend class parameterizedHeaderField::paramsContainer::const_iterator; + friend class parameterizedHeaderField::paramsContainer; + + public: + + typedef std::vector ::iterator::difference_type difference_type; + + iterator(std::vector ::iterator it) : m_iterator(it) { } + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + parameter& operator*() const { return (**m_iterator); } + parameter* operator->() const { return (*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator operator++(int) { iterator i(*this); ++m_iterator; return (i); } + + iterator& operator--() { --m_iterator; return (*this); } + iterator operator--(int) { iterator i(*this); --m_iterator; return (i); } + + iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + iterator operator-(difference_type x) const { return iterator(m_iterator - x); } + + parameter& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector ::iterator m_iterator; + }; + + class const_iterator + { + public: + + typedef std::vector ::const_iterator::difference_type difference_type; + + const_iterator(std::vector ::const_iterator it) : m_iterator(it) { } + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const parameter& operator*() const { return (**m_iterator); } + const parameter* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator operator++(int) { const_iterator i(*this); ++m_iterator; return (i); } + + const_iterator& operator--() { --m_iterator; return (*this); } + const_iterator operator--(int) { const_iterator i(*this); --m_iterator; return (i); } + + const_iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + const_iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + const_iterator operator-(difference_type x) const { return const_iterator(m_iterator - x); } + + const parameter& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector ::const_iterator m_iterator; + }; + + public: + + iterator begin() { return (m_params.begin()); } + iterator end() { return (m_params.end()); } + + const_iterator begin() const { return (const_iterator(m_params.begin())); } + const_iterator end() const { return (const_iterator(m_params.end())); } + + // Parameter insertion + void append(const parameter& param); + void insert(const iterator it, const parameter& param); + + // Parameter removing + void remove(const iterator it); + void clear(); + + // Parameter count + const size_t count() const { return (m_params.size()); } + const size_t size() const { return (m_params.size()); } + + parameter& front() { return (*m_params.front()); } + const parameter& front() const { return (*m_params.front()); } + parameter& back() { return (*m_params.back()); } + const parameter& back() const { return (*m_params.back()); } + + protected: + + std::vector m_params; + + } parameters; + + typedef paramsContainer::iterator iterator; + typedef paramsContainer::const_iterator const_iterator; + +protected: + + std::vector m_params; + +protected: + + virtual void parseValue(const string& buffer, const string::size_type position, const string::size_type end) = 0; + virtual const string generateValue() const = 0; + +public: + + using headerField::parse; + using headerField::generate; + + // No need to override these (use "parseValue" and "generateValue" instead). + // For more information, see "defaultParameter.hpp". + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_PARAMETERIZEDHEADERFIELD_HPP_INCLUDED diff --git a/src/parserHelpers.hpp b/src/parserHelpers.hpp new file mode 100644 index 00000000..41a9f13c --- /dev/null +++ b/src/parserHelpers.hpp @@ -0,0 +1,80 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PARSERHELPERS_HPP_INCLUDED +#define VMIME_PARSERHELPERS_HPP_INCLUDED + + +#include "types.hpp" + +#include + + + +namespace vmime +{ + + +inline const bool isspace(const char_t c) +{ + return (c == ' ' || c == '\t' || c == '\n' || c == '\r'); +} + + +inline const bool isdigit(const char_t c) +{ + return (c >= '0' && c <= '9'); +} + + +inline const bool isalpha(const char_t c) +{ + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); +} + + +inline const char_t tolower(const char_t c) +{ + if (c >= 'A' && c <= 'Z') + return ('a' + (c - 'A')); + else + return c; +} + + +// Checks whether a character is in the 7-bit US-ASCII charset + +inline const bool isascii(const char_t c) +{ + return (c <= 127); +} + + +// Checks whether a character has a visual representation + +inline const bool isprint(const char_t c) +{ + return (c >= 0x20 && c <= 0x7E); +} + + +} // vmime + + +#endif // VMIME_PARSERHELPERS_HPP_INCLUDED diff --git a/src/plainTextPart.cpp b/src/plainTextPart.cpp new file mode 100644 index 00000000..141fb271 --- /dev/null +++ b/src/plainTextPart.cpp @@ -0,0 +1,80 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "plainTextPart.hpp" +#include "header.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +const mediaType plainTextPart::type() const +{ + return (mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN)); +} + + +const int plainTextPart::getPartCount() const +{ + return (1); +} + + +void plainTextPart::generateIn(bodyPart& /* message */, bodyPart& parent) const +{ + // Create a new part + bodyPart* part = new bodyPart(); + parent.body().parts.append(part); + + // Set header fields + part->header().fields.ContentType() = mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN); + part->header().fields.ContentType().charset() = m_charset; + part->header().fields.ContentTransferEncoding() = encoding(encodingTypes::QUOTED_PRINTABLE); + + // Set contents + part->body().contents() = m_text; +} + + +void plainTextPart::parse(const bodyPart& /* message */, + const bodyPart& /* parent */, const bodyPart& textPart) +{ + m_text = textPart.body().contents(); + + try + { + const contentTypeField& ctf = dynamic_cast + (textPart.header().fields.find(headerField::ContentType)); + + m_charset = ctf.charset(); + } + catch (exceptions::no_such_field) + { + // No "Content-type" field. + } + catch (exceptions::no_such_parameter) + { + // No "charset" parameter. + } +} + + +} // vmime diff --git a/src/plainTextPart.hpp b/src/plainTextPart.hpp new file mode 100644 index 00000000..324d2203 --- /dev/null +++ b/src/plainTextPart.hpp @@ -0,0 +1,58 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PLAINTEXTPART_HPP_INCLUDED +#define VMIME_PLAINTEXTPART_HPP_INCLUDED + + +#include "textPart.hpp" + + +namespace vmime +{ + + +class plainTextPart : public textPart +{ +public: + + const mediaType type() const; + + const class charset& charset() const { return (m_charset); } + class charset& charset() { return (m_charset); } + + const contentHandler& text() const { return (m_text); } + contentHandler& text() { return (m_text); } + +protected: + + contentHandler m_text; + class charset m_charset; + + const int getPartCount() const; + + void generateIn(bodyPart& message, bodyPart& parent) const; + void parse(const bodyPart& message, const bodyPart& parent, const bodyPart& textPart); +}; + + +} // vmime + + +#endif // VMIME_PLAINTEXTPART_HPP_INCLUDED diff --git a/src/platformDependant.cpp b/src/platformDependant.cpp new file mode 100644 index 00000000..cf1ed8c1 --- /dev/null +++ b/src/platformDependant.cpp @@ -0,0 +1,35 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "platformDependant.hpp" + + +namespace vmime +{ + + +platformDependant::handler* platformDependant::sm_handler = NULL; + + +platformDependant::handler::~handler() +{ +} + + +} // vmime diff --git a/src/platformDependant.hpp b/src/platformDependant.hpp new file mode 100644 index 00000000..920e970b --- /dev/null +++ b/src/platformDependant.hpp @@ -0,0 +1,155 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PLATFORMDEPENDANT_HPP_INCLUDED +#define VMIME_PLATFORMDEPENDANT_HPP_INCLUDED + + +#include "config.hpp" +#include "dateTime.hpp" +#include "exception.hpp" +#include "charset.hpp" + +#if VMIME_HAVE_MESSAGING_FEATURES + #include "messaging/socket.hpp" + #include "messaging/timeoutHandler.hpp" +#endif + +#if VMIME_HAVE_FILESYSTEM_FEATURES + #include "utility/file.hpp" +#endif + + +namespace vmime +{ + + +/** The link between your application and VMime. It offers an interface to + * access platform-dependant objects: sockets, date/time, file system, etc. + */ + +class platformDependant +{ +public: + + class handler + { + public: + + virtual ~handler(); + + /** Return the current UNIX time (Epoch time): the number of + * seconds elapsed since Jan, 1st 1970 00:00. + * + * @return UNIX Epoch time + */ + virtual const unsigned int getUnixTime() const = 0; + + /** Return the current date and time, in the local time zone. + * + * @return current date and time + */ + virtual const datetime getCurrentLocalTime() const = 0; + + /** Return the host name of the system. + * Used when generating message ids. + * + * @return host name + */ + virtual const string getHostName() const = 0; + + /** Return the current process identifier. + * Used when generating random strings (part boundaries or message ids). + * + * @return current process id + */ + virtual const unsigned int getProcessId() const = 0; + + /** Return the charset used on the system. + * + * @return locale charset + */ + virtual const charset getLocaleCharset() const = 0; + + /** This function is called when VMime library is waiting for + * something (for example, it is called when there is no data + * available in a socket). On POSIX-compliant systems, a + * simple call to sched_yield() should suffice. + */ + virtual void wait() const = 0; + +#if VMIME_HAVE_MESSAGING_FEATURES + /** Return a pointer to a socket factory for the specified socket + * type name (this is user-defined, and used for example when you + * want to set up a SSL connection to a server). + * The returned object will not be deleted by VMime, so it can be + * a pointer to a static object. + * + * @param name socket type name (user-dependant): this is usually + * the value of the property "server.socket-factory" set in the + * session object + * @return socket factory + */ + virtual messaging::socketFactory* getSocketFactory(const string& name = "default") const = 0; + + /** Return a pointer to a timeout-handler factory for the specified name. + * The returned object will not be deleted by VMime, so it can be a + * pointer to a static object. + * + * This is used when you want to handle a timeout-mechanism when + * connecting to messaging servers (please read the documentation to + * learn how to use it). If you are not using time-out handlers, you + * can safely return NULL here. + * + * @param name time-out type name + * @return time-out factory + */ + virtual messaging::timeoutHandlerFactory* getTimeoutHandlerFactory(const string& name = "default") const = 0; +#endif +#if VMIME_HAVE_FILESYSTEM_FEATURES + virtual utility::fileSystemFactory* getFileSystemFactory() const = 0; +#endif + }; + + + template + static void setHandler() + { + delete (sm_handler); + sm_handler = new TYPE; + } + + static const handler* const getHandler() + { + if (!sm_handler) + throw exceptions::no_platform_dependant_handler(); + + return (sm_handler); + } + +private: + + static handler* sm_handler; +}; + + +} // vmime + + +#endif // VMIME_PLATFORMDEPENDANT_HPP_INCLUDED diff --git a/src/propertySet.cpp b/src/propertySet.cpp new file mode 100644 index 00000000..664a2b43 --- /dev/null +++ b/src/propertySet.cpp @@ -0,0 +1,218 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "propertySet.hpp" + + +namespace vmime +{ + + +propertySet::propertySet() +{ +} + + +propertySet::propertySet(const string& props) +{ + parse(props); +} + + +propertySet::propertySet(const propertySet& set) +{ + for (std::list ::const_iterator it = set.m_props.begin() ; it != set.m_props.end() ; ++it) + m_props.push_back(new property(**it)); +} + + +propertySet::~propertySet() +{ + empty(); +} + + +propertySet& propertySet::operator=(const propertySet& set) +{ + empty(); + + for (std::list ::const_iterator it = set.m_props.begin() ; it != set.m_props.end() ; ++it) + m_props.push_back(new property(**it)); + + return (*this); +} + + +void propertySet::set(const string& props) +{ + parse(props); +} + + +void propertySet::empty() +{ + free_container(m_props); +} + + +void propertySet::clear(const string& name) +{ + std::list ::iterator it = std::find_if + (m_props.begin(), m_props.end(), propFinder(name)); + + if (it != m_props.end()) + { + delete (*it); + m_props.erase(it); + } +} + + +void propertySet::parse(const string& props) +{ + const string::const_iterator end = props.end(); + string::const_iterator pos = props.begin(); + + for ( ; pos != end ; ) + { + // Skip white-spaces + for ( ; pos != end && isspace(*pos) ; ++pos); + + if (pos != end) + { + if (*pos == ';') + { + ++pos; + continue; + } + + // Extract the property name + const string::const_iterator optStart = pos; + + for ( ; pos != end && *pos != '=' ; ++pos); + + string::const_iterator optEnd = pos; + + for ( ; optEnd != optStart && isspace(*(optEnd - 1)) ; --optEnd); + + const string option(optStart, optEnd); + string value = "1"; + + if (pos != end) + { + ++pos; // skip '=' + + // Extract the value + for ( ; pos != end && isspace(*pos) ; ++pos); + + if (pos != end) + { + // A quoted-string + if (*pos == '"' || *pos == '\'') + { + value.reserve(50); + + const std::string::value_type quoteChar = *pos; + bool theEnd = false; + bool escape = false; + + for ( ; (pos != end) && !theEnd ; ++pos) + { + if (escape) + { + value += *pos; + escape = false; + } + else + { + if (*pos == '\\') + escape = true; + else if (*pos == quoteChar) + theEnd = true; + else + value += *pos; + } + } + + if (pos != end) + ++pos; + } + // Simple value + else + { + const string::const_iterator valStart = pos; + + for ( ; pos != end && !isspace(*pos) ; ++pos); + + value = string(valStart, pos); + } + + // Advance to the next ';' + for ( ; pos != end && (*pos != ';') ; ++pos); + + if (pos != end) + ++pos; // skip ';' + } + } + + m_props.push_back(new property(option, value)); + } + } +} + + +template <> +void propertySet::property::set(const string& value) +{ + m_value = value; +} + + +template <> +void propertySet::property::set(const bool& value) +{ + m_value = value ? "true" : "false"; +} + + +template <> +const string propertySet::property::get() const +{ + return (m_value); +} + + +template <> +const bool propertySet::property::get() const +{ + if (toLower(m_value) == "true") + return true; + else + { + int val = 0; + + std::istringstream iss(m_value); + iss >> val; + + return (!iss.fail() && val != 0); + } +} + + +} // vmime diff --git a/src/propertySet.hpp b/src/propertySet.hpp new file mode 100644 index 00000000..eb0ee7d7 --- /dev/null +++ b/src/propertySet.hpp @@ -0,0 +1,340 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_PROPERTY_HPP_INCLUDED +#define VMIME_PROPERTY_HPP_INCLUDED + + +#include +#include +#include +#include + +#include "base.hpp" +#include "exception.hpp" + + +namespace vmime +{ + + +class propertySet +{ +private: + + class property + { + public: + + property(const string& name, const string& value) : m_name(name), m_value(value) { } + property(const string& name) : m_name(name) { } + property(const property& prop) : m_name(prop.m_name), m_value(prop.m_value) { } + + const string& name() const { return (m_name); } + const string& value() const { return (m_value); } + + template void set(const TYPE& value); + template const TYPE get() const; + + private: + + string m_name; + string m_value; + }; + + + class propertyProxy + { + public: + + propertyProxy(const string& name, propertySet* set) + : m_name(name), m_set(set) + { + } + + template + propertyProxy& operator=(const TYPE& value) + { + m_set->set(m_name, value); + return (*this); + } + + template + void set(const TYPE& value) + { + m_set->set(m_name, value); + } + + template + const TYPE get() const + { + return (m_set->get (m_name)); + } + + operator string() const + { + return (m_set->get (m_name)); + } + + private: + + const string m_name; + propertySet* m_set; + }; + + class constPropertyProxy + { + public: + + constPropertyProxy(const string& name, const propertySet* set) + : m_name(name), m_set(set) + { + } + + template + const TYPE get() const + { + return (m_set->get (m_name)); + } + + operator string() const + { + return (m_set->get (m_name)); + } + + private: + + const string m_name; + const propertySet* m_set; + }; + +public: + + propertySet(); + propertySet(const string& props); + propertySet(const propertySet& set); + + ~propertySet(); + + propertySet& operator=(const propertySet& set); + + void set(const string& props); + + void empty(); + + void clear(const string& name); + + + const bool exists(const string& name) const + { + return (find(name) != NULL); + } + + template + const TYPE get(const string& name) const + { + const property* const prop = find(name); + if (!prop) throw exceptions::no_such_property(name); + + return (prop->get ()); + } + + template + const TYPE get(const string& name, const TYPE defaultValue) const + { + const property* const prop = find(name); + return (prop ? prop->get () : defaultValue); + } + + template + void set(const string& name, const TYPE& value) + { + findOrCreate(name)->set(value); + } + + propertyProxy operator[](const string& name) + { + return (propertyProxy(name, this)); + } + + const constPropertyProxy operator[](const string& name) const + { + return (constPropertyProxy(name, this)); + } + +private: + + void parse(const string& props); + + + class propFinder : public std::unary_function + { + public: + + propFinder(const string& name) : m_name(toLower(name)) { } + + const bool operator()(property* const p) const + { + return (toLower(p->name()) == m_name); + } + + private: + + const std::string m_name; + }; + + property* find(const string& name) const + { + std::list ::const_iterator it = std::find_if + (m_props.begin(), m_props.end(), propFinder(name)); + + return (it != m_props.end() ? *it : NULL); + } + + property* findOrCreate(const string& name) + { + std::list ::const_iterator it = std::find_if + (m_props.begin(), m_props.end(), propFinder(name)); + + if (it != m_props.end()) + { + return (*it); + } + else + { + property* prop = new property(name, ""); + m_props.push_back(prop); + return (prop); + } + } + + typedef std::list list_type; + list_type m_props; + +public: + + class iterator; + + class const_iterator + { + friend class propertySet; + + public: + + const_iterator() { } + const_iterator(const const_iterator& it) : m_it(it.m_it) { } + const_iterator(const iterator& it) : m_it(it.m_it) { } + + const_iterator& operator=(const const_iterator& it) { m_it = it.m_it; return (*this); } + + const property& operator*() const { return (**m_it); } + const property* operator->() const { return (*m_it); } + + const_iterator& operator++() { ++m_it; return (*this); } + const_iterator operator++(int) { return (m_it++); } + + const_iterator& operator--() { --m_it; return (*this); } + const_iterator operator--(int) { return (m_it--); } + + const bool operator==(const const_iterator& it) const { return (m_it == it.m_it); } + const bool operator!=(const const_iterator& it) const { return (m_it != it.m_it); } + + private: + + const_iterator(const list_type::const_iterator it) : m_it(it) { } + + list_type::const_iterator m_it; + }; + + class iterator + { + friend class propertySet; + friend class propertySet::const_iterator; + + public: + + iterator() { } + iterator(const iterator& it) : m_it(it.m_it) { } + + iterator& operator=(const iterator& it) { m_it = it.m_it; return (*this); } + + property& operator*() const { return (**m_it); } + property* operator->() const { return (*m_it); } + + iterator& operator++() { ++m_it; return (*this); } + iterator operator++(int) { return (m_it++); } + + iterator& operator--() { --m_it; return (*this); } + iterator operator--(int) { return (m_it--); } + + const bool operator==(const iterator& it) const { return (m_it == it.m_it); } + const bool operator!=(const iterator& it) const { return (m_it != it.m_it); } + + private: + + iterator(const list_type::iterator it) : m_it(it) { } + + list_type::iterator m_it; + }; + + iterator begin() { return iterator(m_props.begin()); } + iterator end() { return iterator(m_props.end()); } + + const_iterator begin() const { return const_iterator(m_props.begin()); } + const_iterator end() const { return const_iterator(m_props.end()); } +}; + + + +template +void propertySet::property::set(const TYPE& value) +{ + std::ostringstream oss; + oss << value; + + m_value = oss.str(); +} + + +template +const TYPE propertySet::property::get() const +{ + TYPE val = TYPE(); + + std::istringstream iss(m_value); + iss >> val; + + if (iss.fail()) + throw exceptions::invalid_property_type(); + + return (val); +} + + +template <> void propertySet::property::set(const string& value); +template <> void propertySet::property::set(const bool& value); + +template <> const string propertySet::property::get() const; +template <> const bool propertySet::property::get() const; + + +} // vmime + + +#endif // VMIME_PROPERTY_HPP_INCLUDED diff --git a/src/relayField.cpp b/src/relayField.cpp new file mode 100644 index 00000000..b7959547 --- /dev/null +++ b/src/relayField.cpp @@ -0,0 +1,239 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "relayField.hpp" +#include "text.hpp" +#include "parserHelpers.hpp" + +#include + + +namespace vmime +{ + + +relayField::relayField() +{ +} + + +/* + + RFC #2822: + + received = "Received" ":" ; one per relay + ["from" domain] ; sending host + ["by" domain] ; receiving host + ["via" atom] ; physical path + *("with" atom) ; link/mail protocol + ["id" msg-id] ; receiver msg id + ["for" addr-spec] ; initial form +*/ + +void relayField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* const pstart = buffer.data() + position; + const string::value_type* p = pend - 1; + + // Find the beginning of the date part + while (p >= pstart && *p != ';') + --p; + + if (p >= pstart) + { + // Parse the date/time part + m_date.parse(buffer, position + (p - pstart) + 1, end); + + // Parse the components + std::istringstream iss(string + (buffer.begin() + position, buffer.begin() + position + (p - pstart))); + + string word; + std::vector previous; + + enum Parts + { + Part_None, + Part_From, // The "from" part + Part_By, // The "by" part + Part_Via, // The "via" part + Part_With, // One "with" part + Part_Id, // The "id" part + Part_For, // The "for" part + Part_End + }; + + Parts part = Part_None; + bool cont = true; + bool inComment = false; + + while (cont) + { + Parts newPart = Part_None; + + if (cont = (iss >> word)) + { + // A little hack for handling comments + if (inComment) + { + string::size_type par = word.find(')'); + + if (par != string::npos) + { + previous.push_back(string(word.begin(), word.begin() + par + 1)); + word.erase(word.begin(), word.begin() + par + 1); + inComment = false; + } + } + + bool keyword = false; + + if (!inComment) + { + if (isStringEqualNoCase(word, "from", 4)) + { + newPart = Part_From; + keyword = true; + } + else if (isStringEqualNoCase(word, "by", 2)) + { + newPart = Part_By; + keyword = true; + } + else if (isStringEqualNoCase(word, "via", 2)) + { + newPart = Part_Via; + keyword = true; + } + else if (isStringEqualNoCase(word, "with", 2)) + { + newPart = Part_With; + keyword = true; + } + else if (isStringEqualNoCase(word, "id", 2)) + { + newPart = Part_Id; + keyword = true; + } + else if (isStringEqualNoCase(word, "for", 2)) + { + newPart = Part_For; + keyword = true; + } + } + + if (!keyword) + { + if (word.find('(') != string::npos) + inComment = true; + + previous.push_back(word); + } + } + + if (!cont || newPart != Part_None) + { + if (part != Part_None) + { + std::ostringstream value; + + for (std::vector ::const_iterator + it = previous.begin() ; it != previous.end() ; ++it) + { + if (it != previous.begin()) value << " "; + value << *it; + } + + switch (part) + { + case Part_From: m_from = value.str(); break; + case Part_By: m_by = value.str(); break; + case Part_Via: m_via = value.str(); break; + case Part_With: m_with.push_back(value.str()); break; + case Part_Id: m_id = value.str(); break; + case Part_For: m_for = value.str(); break; + default: break; // Should never happen... + } + } + + previous.clear(); + part = newPart; + } + } + } + + if (newPosition) + *newPosition = end; +} + + +void relayField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + std::ostringstream oss; + int count = 0; + + if (m_from.length()) oss << (count++ > 0 ? " " : "") << "from " << m_from; + if (m_by.length()) oss << (count++ > 0 ? " " : "") << "by " << m_by; + if (m_via.length()) oss << (count++ > 0 ? " " : "") << "via " << m_via; + + for (std::vector ::const_iterator + it = m_with.begin() ; it != m_with.end() ; ++it) + { + oss << (count++ > 0 ? " " : "") << "with " << *it; + } + + if (m_id.length()) oss << (count++ > 0 ? " " : "") << "id " << m_id; + if (m_for.length()) oss << (count++ > 0 ? " " : "") << "for " << m_for; + + oss << "; " << m_date.generate(); + + string result(oss.str()); + + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + encodeAndFoldText(os, text(result), maxLineLength, + pos, newLinePos, encodeAndFoldFlags::forceNoEncoding); +} + + +void relayField::copyFrom(const headerField& field) +{ + const relayField& source = dynamic_cast(field); + + m_from = source.m_from; + m_via = source.m_via; + m_by = source.m_by; + m_id = source.m_id; + m_for = source.m_for; + + m_with.resize(source.m_with.size()); + std::copy(source.m_with.begin(), source.m_with.end(), m_with.begin()); + + m_date = source.m_date; + + headerField::copyFrom(field); +} + + +} // vmime diff --git a/src/relayField.hpp b/src/relayField.hpp new file mode 100644 index 00000000..f24be873 --- /dev/null +++ b/src/relayField.hpp @@ -0,0 +1,93 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_RELAYFIELD_HPP_INCLUDED +#define VMIME_RELAYFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "dateTime.hpp" + + +namespace vmime +{ + + +class relayField : public headerField +{ + friend class headerFieldFactory::registerer ; + +protected: + + relayField(); + +public: + + void copyFrom(const headerField& field); + + const string& from() const { return (m_from); } + string& from() { return (m_from); } + + const string& via() const { return (m_via); } + string& via() { return (m_via); } + + const string& by() const { return (m_by); } + string& by() { return (m_by); } + + const string& id() const { return (m_id); } + string& id() { return (m_id); } + + const string& for_() const { return (m_for); } + string& for_() { return (m_for); } + + const datetime& date() const { return (m_date); } + datetime& date() { return (m_date); } + + const std::vector & with() const { return (m_with); } + std::vector & with() { return (m_with); } + +protected: + + string m_from; + string m_via; + string m_by; + string m_id; + string m_for; + std::vector m_with; + + datetime m_date; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_RELAYFIELD_HPP_INCLUDED diff --git a/src/text.cpp b/src/text.cpp new file mode 100644 index 00000000..db993136 --- /dev/null +++ b/src/text.cpp @@ -0,0 +1,240 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "text.hpp" + + +namespace vmime +{ + + +text::text() +{ +} + + +text::text(const text& t) +{ + operator=(t); +} + + +text::text(const string& t, const charset& ch) +{ + makeWordsFromText(t, ch, *this); +} + + +text::text(const string& t) +{ + makeWordsFromText(t, charset::getLocaleCharset(), *this); +} + + +text::text(const word& w) +{ + append(w); +} + + +text::~text() +{ + clear(); +} + + +#if VMIME_WIDE_CHAR_SUPPORT + +const wstring text::getDecodedText() const +{ + wstring out; + + for (std::vector ::const_iterator i = m_words.begin() ; i != m_words.end() ; ++i) + { + out += (*i)->getDecodedText(); + } + + return (out); +} + +#endif + + +void text::append(const word& w) +{ + m_words.push_back(new word(w)); +} + + +void text::insert(const iterator it, const word& w) +{ + m_words.insert(it.m_iterator, new word(w)); +} + + +void text::clear() +{ + free_container(m_words); + + m_words.clear(); +} + + +void text::remove(const iterator it) +{ + delete (*it.m_iterator); + m_words.erase(it.m_iterator); +} + + +text& text::operator=(const text& t) +{ + clear(); + + for (std::vector ::const_iterator i = t.m_words.begin() ; i != t.m_words.end() ; ++i) + m_words.push_back(new word(**i)); + + return (*this); +} + + +const bool text::operator==(const text& t) const +{ + if (size() == t.size()) + { + bool equal = false; + + std::vector ::const_iterator i = m_words.begin(); + std::vector ::const_iterator j = t.m_words.begin(); + + for ( ; equal && i != m_words.end() ; ++i, ++j) + equal = (*i == *j); + + return (equal); + } + + return (false); +} + + +const bool text::operator!=(const text& t) const +{ + return !(*this == t); +} + + +/** Return the text converted into the specified charset. + * The encoded-words are decoded and then converted in the + * destination charset. + * + * @param dest output charset + * @return text decoded in the specified charset + */ + +const string text::getConvertedText(const charset& dest) const +{ + string out; + + for (std::vector ::const_iterator i = m_words.begin() ; i != m_words.end() ; ++i) + { + out += (*i)->getConvertedText(dest); + } + + return (out); +} + + +/** Check whether the list of encoded-words is empty. + * + * @return true if the list contains no encoded-word, false otherwise + */ + +const bool text::empty() const +{ + return (m_words.size() == 0); +} + + +/** Return the number of encoded-words in the list. + * + * @return number of encoded-words + */ + +const size_t text::count() const +{ + return (m_words.size()); +} + + +/** Return the number of encoded-words in the list. + * + * @return number of encoded-words + */ + +const size_t text::size() const +{ + return (m_words.size()); +} + + +/** Return the first encoded-word of the list. + * + * @return first encoded-word + */ + +word& text::front() +{ + return (*m_words.front()); +} + + +/** Return the first encoded-word of the list. + * + * @return first encoded-word + */ + +const word& text::front() const +{ + return (*m_words.front()); +} + + +/** Return the last encoded-word of the list. + * + * @return last encoded-word + */ + +word& text::back() +{ + return (*m_words.back()); +} + + +/** Return the last encoded-word of the list. + * + * @return last encoded-word + */ + +const word& text::back() const +{ + return (*m_words.back()); +} + + +} // vmime diff --git a/src/text.hpp b/src/text.hpp new file mode 100644 index 00000000..e6339ab9 --- /dev/null +++ b/src/text.hpp @@ -0,0 +1,176 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_TEXT_HPP_INCLUDED +#define VMIME_TEXT_HPP_INCLUDED + + +#include "base.hpp" +#include "word.hpp" + + +namespace vmime +{ + + +/** A class representing a list of encoded-words, as defined + * in RFC-2047 (basic type). + */ + +class text +{ +public: + + text(); + text(const text& t); + text(const string& t, const charset& ch); + explicit text(const string& t); + explicit text(const word& w); + ~text(); + +public: + + text& operator=(const text& t); + + const bool operator==(const text& t) const; + const bool operator!=(const text& t) const; + + // Words iterator + class const_iterator; + + class iterator + { + friend class text::const_iterator; + friend class text; + + public: + + typedef std::vector ::iterator::difference_type difference_type; + + iterator(std::vector ::iterator it) : m_iterator(it) { } + iterator(const iterator& it) : m_iterator(it.m_iterator) { } + + iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + word& operator*() const { return (**m_iterator); } + word* operator->() const { return (*m_iterator); } + + iterator& operator++() { ++m_iterator; return (*this); } + iterator operator++(int) { iterator i(*this); ++m_iterator; return (i); } + + iterator& operator--() { --m_iterator; return (*this); } + iterator operator--(int) { iterator i(*this); --m_iterator; return (i); } + + iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + iterator operator+(difference_type x) const { return iterator(m_iterator + x); } + iterator operator-(difference_type x) const { return iterator(m_iterator - x); } + + word& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector ::iterator m_iterator; + }; + + class const_iterator + { + public: + + typedef std::vector ::const_iterator::difference_type difference_type; + + const_iterator(std::vector ::const_iterator it) : m_iterator(it) { } + const_iterator(const iterator& it) : m_iterator(it.m_iterator) { } + const_iterator(const const_iterator& it) : m_iterator(it.m_iterator) { } + + const_iterator& operator=(const const_iterator& it) { m_iterator = it.m_iterator; return (*this); } + const_iterator& operator=(const iterator& it) { m_iterator = it.m_iterator; return (*this); } + + const word& operator*() const { return (**m_iterator); } + const word* operator->() const { return (*m_iterator); } + + const_iterator& operator++() { ++m_iterator; return (*this); } + const_iterator operator++(int) { const_iterator i(*this); ++m_iterator; return (i); } + + const_iterator& operator--() { --m_iterator; return (*this); } + const_iterator operator--(int) { const_iterator i(*this); --m_iterator; return (i); } + + const_iterator& operator+=(difference_type n) { m_iterator += n; return (*this); } + const_iterator& operator-=(difference_type n) { m_iterator -= n; return (*this); } + + const_iterator operator+(difference_type x) const { return const_iterator(m_iterator + x); } + const_iterator operator-(difference_type x) const { return const_iterator(m_iterator - x); } + + const word& operator[](difference_type n) const { return *(m_iterator[n]); } + + const bool operator==(const const_iterator& it) const { return (it.m_iterator == m_iterator); } + const bool operator!=(const const_iterator& it) const { return (!(*this == it)); } + + protected: + + std::vector ::const_iterator m_iterator; + }; + + + iterator begin() { return (m_words.begin()); } + iterator end() { return (m_words.end()); } + + const_iterator begin() const { return (const_iterator(m_words.begin())); } + const_iterator end() const { return (const_iterator(m_words.end())); } + + const word& operator[](const std::vector ::size_type x) const { return (*m_words[x]); } + word& operator[](const std::vector ::size_type x) { return (*m_words[x]); } + + // Word manipulation + void append(const word& w); + void insert(const iterator it, const word& w); + + void clear(); + void remove(const iterator it); + + // Word count + const bool empty() const; + const size_t count() const; + const size_t size() const; + + word& front(); + const word& front() const; + word& back(); + const word& back() const; + + // Decoding +#if VMIME_WIDE_CHAR_SUPPORT + const wstring getDecodedText() const; +#endif + const string getConvertedText(const charset& dest) const; + +protected: + + std::vector m_words; +}; + + +} // vmime + + +#endif // VMIME_TEXT_HPP_INCLUDED diff --git a/src/textField.cpp b/src/textField.cpp new file mode 100644 index 00000000..0a316af9 --- /dev/null +++ b/src/textField.cpp @@ -0,0 +1,69 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "textField.hpp" + + +namespace vmime +{ + + +textField::textField() +{ +} + + +void textField::parse(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + decodeAndUnfoldText(buffer.begin() + position, buffer.begin() + end, m_text); + + if (newPosition) + *newPosition = end; +} + + +void textField::generate(utility::outputStream& os, const string::size_type maxLineLength, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string::size_type pos = curLinePos; + + headerField::generate(os, maxLineLength, pos, &pos); + + encodeAndFoldText(os, m_text, maxLineLength, pos, newLinePos, encodeAndFoldFlags::none); +} + + +void textField::copyFrom(const headerField& field) +{ + const textField& source = dynamic_cast(field); + m_text = source.m_text; + + headerField::copyFrom(field); +} + + +textField& textField::operator=(const text& value) +{ + m_text = value; + return (*this); +} + + +} // vmime diff --git a/src/textField.hpp b/src/textField.hpp new file mode 100644 index 00000000..1e672202 --- /dev/null +++ b/src/textField.hpp @@ -0,0 +1,70 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_TEXTFIELD_HPP_INCLUDED +#define VMIME_TEXTFIELD_HPP_INCLUDED + + +#include "base.hpp" +#include "component.hpp" + +#include "headerFieldFactory.hpp" +#include "text.hpp" + + +namespace vmime +{ + + +class textField : public headerField +{ + friend class headerFieldFactory::registerer ; + +protected: + + textField(); + +public: + + void copyFrom(const headerField& field); + + textField& operator=(const text& value); + + const text& value() const { return (m_text); } + text& value() { return (m_text); } + +protected: + + text m_text; + +public: + + using headerField::parse; + using headerField::generate; + + // Component parsing & assembling + void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +}; + + +} // vmime + + +#endif // VMIME_TEXTFIELD_HPP_INCLUDED diff --git a/src/textParameter.cpp b/src/textParameter.cpp new file mode 100644 index 00000000..402d8d1f --- /dev/null +++ b/src/textParameter.cpp @@ -0,0 +1,50 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "textParameter.hpp" +#include "parserHelpers.hpp" + + +namespace vmime +{ + + +void textParameter::parseValue(const string& buffer, const string::size_type position, + const string::size_type end) +{ + m_value = string(buffer.begin() + position, buffer.begin() + end); +} + + +const string textParameter::generateValue() const +{ + return (m_value); +} + + +void textParameter::copyFrom(const parameter& param) +{ + const textParameter& source = dynamic_cast(param); + m_value = source.m_value; + + defaultParameter::copyFrom(param); +} + + +} // vmime diff --git a/src/textParameter.hpp b/src/textParameter.hpp new file mode 100644 index 00000000..a3332a84 --- /dev/null +++ b/src/textParameter.hpp @@ -0,0 +1,54 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_TEXTPARAMETER_HPP_INCLUDED +#define VMIME_TEXTPARAMETER_HPP_INCLUDED + + +#include "defaultParameter.hpp" + + +namespace vmime +{ + + +class textParameter : public defaultParameter +{ +protected: + + string m_value; + +public: + + void copyFrom(const parameter& param); + + const string& value() const { return (m_value); } + string& value() { return (m_value); } + +protected: + + void parseValue(const string& buffer, const string::size_type position, const string::size_type end); + const string generateValue() const; +}; + + +} // vmime + + +#endif // VMIME_TEXTPARAMETER_HPP_INCLUDED diff --git a/src/textPart.hpp b/src/textPart.hpp new file mode 100644 index 00000000..ed07f0b7 --- /dev/null +++ b/src/textPart.hpp @@ -0,0 +1,66 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_TEXTPART_HPP_INCLUDED +#define VMIME_TEXTPART_HPP_INCLUDED + + +#include "bodyPart.hpp" + +#include "mediaType.hpp" +#include "charset.hpp" +#include "contentHandler.hpp" + + +namespace vmime +{ + + +class textPart +{ + friend class textPartFactory; + friend class messageBuilder; // for generateIn, getPartCount + friend class messageParser; // for parse + +public: + + virtual ~textPart() { } + + + virtual const mediaType type() const = 0; + + virtual const class charset& charset() const = 0; + virtual class charset& charset() = 0; + + virtual const contentHandler& text() const = 0; + virtual contentHandler& text() = 0; + +protected: + + virtual const int getPartCount() const = 0; + + virtual void generateIn(bodyPart& message, bodyPart& parent) const = 0; + virtual void parse(const bodyPart& message, const bodyPart& parent, const bodyPart& textPart) = 0; +}; + + +} // vmime + + +#endif // VMIME_TEXTPART_HPP_INCLUDED diff --git a/src/textPartFactory.cpp b/src/textPartFactory.cpp new file mode 100644 index 00000000..c8fc37eb --- /dev/null +++ b/src/textPartFactory.cpp @@ -0,0 +1,60 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "textPartFactory.hpp" +#include "exception.hpp" + + +#include "plainTextPart.hpp" +#include "htmlTextPart.hpp" + + +namespace vmime +{ + + +textPartFactory::textPartFactory() +{ + // Register some default names + registerType (mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN)); + registerType (mediaType(mediaTypes::TEXT, mediaTypes::TEXT_HTML)); +} + + +textPartFactory::~textPartFactory() +{ +} + + +textPart* textPartFactory::create(const mediaType& type) +{ + NameMap::const_iterator pos = m_nameMap.find(type.generate()); + + if (pos != m_nameMap.end()) + { + return ((*pos).second)(); + } + else + { + throw exceptions::no_factory_available(); + } +} + + +} // vmime diff --git a/src/textPartFactory.hpp b/src/textPartFactory.hpp new file mode 100644 index 00000000..15969c93 --- /dev/null +++ b/src/textPartFactory.hpp @@ -0,0 +1,74 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_TEXTPARTFACTORY_HPP_INCLUDED +#define VMIME_TEXTPARTFACTORY_HPP_INCLUDED + + +#include "textPart.hpp" +#include "mediaType.hpp" +#include "utility/singleton.hpp" + + +namespace vmime +{ + + +class textPartFactory : public utility::singleton +{ + friend class utility::singleton ; + +protected: + + textPartFactory(); + ~textPartFactory(); + + typedef textPart* (*AllocFunc)(void); + typedef std::map NameMap; + + NameMap m_nameMap; + + template + class registerer + { + public: + + static textPart* creator() + { + // Allocate a new object + return new TYPE(); + } + }; + +public: + + template + void registerType(const mediaType& type) + { + m_nameMap.insert(NameMap::value_type(toLower(type.generate()), ®isterer::creator)); + } + + textPart* create(const mediaType& type); +}; + + +} // vmime + + +#endif // VMIME_TEXTPARTFACTORY_HPP_INCLUDED diff --git a/src/types.hpp b/src/types.hpp new file mode 100644 index 00000000..fa37c6dc --- /dev/null +++ b/src/types.hpp @@ -0,0 +1,43 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_TYPES_HPP_INCLUDED +#define VMIME_TYPES_HPP_INCLUDED + + +#include +#include + +#include "config.hpp" + + +namespace vmime +{ + typedef std::string string; +#if VMIME_WIDE_CHAR_SUPPORT + typedef std::wstring wstring; +#endif + + typedef unsigned short port_t; + + typedef int char_t; +} + + +#endif // VMIME_TYPES_HPP_INCLUDED diff --git a/src/utility/file.hpp b/src/utility/file.hpp new file mode 100644 index 00000000..f4d63bc2 --- /dev/null +++ b/src/utility/file.hpp @@ -0,0 +1,218 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_FILE_HPP_INCLUDED +#define VMIME_UTILITY_FILE_HPP_INCLUDED + + +#include "path.hpp" +#include "../config.hpp" + + +#if VMIME_HAVE_FILESYSTEM_FEATURES + + +namespace vmime { +namespace utility { + + +class file; + + +/** File list iterator (see file::getFiles). + */ + +class fileIterator +{ +public: + + virtual ~fileIterator() { } + + /** Check whether the cursor has reach the end of the list. + * + * @return true if you can call nextElement(), or false + * if no more file is available + */ + virtual const bool hasMoreElements() const = 0; + + /** Return the next file in the list. + * + * @return next file or NULL + */ + virtual file* nextElement() = 0; +}; + + +// TODO: fileWriter + +class fileWriter +{ +public: + + virtual ~fileWriter() { } + + virtual utility::outputStream* getOutputStream() = 0; +}; + + +// TODO: fileReader + +class fileReader +{ +public: + + virtual ~fileReader() { } + + virtual utility::inputStream* getInputStream() = 0; +}; + + +/** Abstract representation of a file or directory. + */ + +class file +{ +public: + + typedef utility::path path; + typedef long length_type; + + + virtual ~file() { } + + + /** Create the file pointed by this file object. + */ + virtual void createFile() = 0; + + /** Create the directory pointed by this file object. + * + * @param createAll if set to true, recursively create all + * parent directories if they do not exist + */ + virtual void createDirectory(const bool createAll = false) = 0; + + /** Test whether this is a file. + * + * @return true if this is a file, false otherwise + */ + virtual const bool isFile() const = 0; + + /** Test whether this is a directory. + * + * @return true if this is a directory, false otherwise + */ + virtual const bool isDirectory() const = 0; + + /** Test whether this file is readible. + * + * @return true if we can read this file, false otherwise + */ + virtual const bool canRead() const = 0; + + /** Test whether this file is writeable. + * + * @return true if we can write to this file, false otherwise + */ + virtual const bool canWrite() const = 0; + + /** Return the length of this file. + * + * @return file size (in bytes) + */ + virtual const length_type length() = 0; + + /** Return the full path of this file/directory. + * + * @return full path of the file + */ + virtual const path& fullPath() const = 0; + + /** Test whether this file/directory exists. + * + * @return true if the file exists, false otherwise + */ + virtual const bool exists() const = 0; + + /** Return the parent directory of this file/directory. + * + * @return parent directory (or NULL if root) + */ + virtual const file* getParent() const = 0; + + /** Rename the file/directory. + * + * @param newName full path of the new file + */ + virtual void rename(const path& newName) = 0; + + /** Deletes this file/directory. + */ + virtual void remove() = 0; + + + // TODO virtual fileWriter* getFileWriter() = 0; + // TODO virtual fileReader* getFileReader() = 0; + + /** Enumerate files contained in this directory. + * + * @return file iterator to enumerate files + * @throw exceptions::not_a_directory if this is not a directory + */ + virtual fileIterator* getFiles() const; +}; + + +class fileSystemFactory +{ +public: + + virtual ~fileSystemFactory() { } + + /** Create a new file object from the specified path. + * + * @param path full path (absolute) of the file + * @return new file object for the path + */ + virtual file* create(const file::path& path) = 0; + + /** Parse a path contained in a string. + * + * @param str string containing a path in a system-dependant representation + * @return path object (abstract representation) + */ + virtual file::path stringToPath(const string& str) = 0; + + /** Return the system-dependant string representation for the specified path. + * + * @param path abstract representation of the path + * @return string representation of the path + */ + virtual string pathToString(const file::path& path) = 0; +}; + + +} // utility +} // vmime + + +#endif // VMIME_HAVE_FILESYSTEM_FEATURES + + +#endif // VMIME_UTILITY_FILE_HPP_INCLUDED diff --git a/src/utility/md5.cpp b/src/utility/md5.cpp new file mode 100644 index 00000000..e181a5d0 --- /dev/null +++ b/src/utility/md5.cpp @@ -0,0 +1,331 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// +// Derived from cryptoapi implementation, originally based on the +// public domain implementation written by Colin Plumb in 1993. +// +// Copyright (C) Cryptoapi developers. +// +// Algorithm Copyright: +// +// Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +// rights reserved. +// +// License to copy and use this software is granted provided that it +// is identified as the "RSA Data Security, Inc. MD5 Message-Digest +// Algorithm" in all material mentioning or referencing this software +// or this function. +// +// License is also granted to make and use derivative works provided +// that such works are identified as "derived from the RSA Data +// Security, Inc. MD5 Message-Digest Algorithm" in all material +// mentioning or referencing the derived work. +// +// RSA Data Security, Inc. makes no representations concerning either +// the merchantability of this software or the suitability of this +// software forany particular purpose. It is provided "as is" +// without express or implied warranty of any kind. +// These notices must be retained in any copies of any part of this +// documentation and/or software. + +#include "md5.hpp" + + +namespace vmime { +namespace utility { + + +md5::md5() + : m_finalized(false) +{ + init(); +} + + +md5::md5(const vmime_uint8* const in, const unsigned long length) + : m_finalized(false) +{ + init(); + update(in, length); +} + + +md5::md5(const string& in) + : m_finalized(false) +{ + init(); + update((vmime_uint8*) in.c_str(), in.length()); +} + + +void md5::init() +{ + m_hash[0] = 0x67452301; + m_hash[1] = 0xefcdab89; + m_hash[2] = 0x98badcfe; + m_hash[3] = 0x10325476; + + m_byteCount = 0; +} + + +static void copyUint8Array(vmime_uint8* dest, const vmime_uint8* src, unsigned long count) +{ + for ( ; count >= 4 ; count -= 4, dest += 4, src += 4) + { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; + } + + for ( ; count ; --count, ++dest, ++src) + dest[0] = src[0]; +} + + +void md5::update(const string& in) +{ + update((vmime_uint8*) in.c_str(), in.length()); +} + + +void md5::update(const vmime_uint8* data, unsigned long len) +{ + if (m_finalized) + return; + + const unsigned long avail = 64 - (m_byteCount & 0x3f); + + m_byteCount += len; + + if (avail > len) + { + copyUint8Array(m_block + (64 - avail), data, len); + return; + } + + copyUint8Array(m_block + (64 - avail), data, avail); + transformHelper(); + + data += avail; + len -= avail; + + while (len >= 64) + { + copyUint8Array(m_block, data, 64); + transformHelper(); + + data += 64; + len -= 64; + } + + copyUint8Array(m_block, data, len); +} + + +void md5::finalize() +{ + const long offset = m_byteCount & 0x3f; + + vmime_uint8* p = m_block + offset; + long padding = 56 - (offset + 1); + + *p++ = 0x80; + + if (padding < 0) + { + memset(p, 0x00, padding + 8); + transformHelper(); + p = m_block; + padding = 56; + } + + memset(p, 0, padding); + + ((vmime_uint32*) m_block)[14] = (m_byteCount << 3); + ((vmime_uint32*) m_block)[15] = (m_byteCount >> 29); + +#if VMIME_BYTE_ORDER_BIG_ENDIAN + swapUint32Array((vmime_uint32*) m_block, (64 - 8) / 4); +#endif + + transform(); + +#if VMIME_BYTE_ORDER_BIG_ENDIAN + swapUint32Array((vmime_uint32*) m_hash, 4); +#endif + + m_finalized = true; +} + + +static inline vmime_uint32 swapUint32(const vmime_uint32 D) +{ + return ((D << 24) | ((D << 8) & 0x00FF0000) | ((D >> 8) & 0x0000FF00) | (D >> 24)); +} + + +static inline void swapUint32Array(vmime_uint32* buf, unsigned long words) +{ + for ( ; words >= 4 ; words -= 4, buf += 4) + { + buf[0] = swapUint32(buf[0]); + buf[1] = swapUint32(buf[1]); + buf[2] = swapUint32(buf[2]); + buf[3] = swapUint32(buf[3]); + } + + for ( ; words ; --words, ++buf) + buf[0] = swapUint32(buf[0]); +} + + +void md5::transformHelper() +{ +#if VMIME_BYTE_ORDER_BIG_ENDIAN + swapUint32Array((vmime_uint32*) m_block, 64 / 4); +#endif + transform(); +} + + +void md5::transform() +{ + const vmime_uint32* const in = (vmime_uint32*) m_block; + + vmime_uint32 a = m_hash[0]; + vmime_uint32 b = m_hash[1]; + vmime_uint32 c = m_hash[2]; + vmime_uint32 d = m_hash[3]; + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define MD5STEP(f, w, x, y, z, in, s) \ + (w += f(x, y, z) + in, w = (w<>(32-s)) + x) + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + m_hash[0] += a; + m_hash[1] += b; + m_hash[2] += c; + m_hash[3] += d; +} + + +const string md5::hex() +{ + if (!m_finalized) + finalize(); + + static const unsigned char hex[] = "0123456789abcdef"; + + std::ostringstream oss; + const vmime_uint8* const digest = (vmime_uint8*) m_hash; + + for (int i = 0 ; i < 16 ; ++i) + { + oss << hex[(digest[i] & 0xf0) >> 4]; + oss << hex[(digest[i] & 0x0f)]; + } + + return (oss.str()); +} + + +const vmime_uint8* md5::hash() +{ + if (!m_finalized) + finalize(); + + return ((vmime_uint8*) m_hash); +} + + +} // utility +} // vmime diff --git a/src/utility/md5.hpp b/src/utility/md5.hpp new file mode 100644 index 00000000..dc9bb384 --- /dev/null +++ b/src/utility/md5.hpp @@ -0,0 +1,68 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_MD5_HPP_INCLUDED +#define VMIME_UTILITY_MD5_HPP_INCLUDED + + +#include "base.hpp" +#include "config.hpp" + + +namespace vmime { +namespace utility { + + +class md5 +{ +public: + + md5(); + md5(const vmime_uint8* const in, const unsigned long length); + md5(const string& in); + +public: + + const string hex(); + const vmime_uint8* hash(); + + void update(const vmime_uint8* data, unsigned long len); + void update(const string& in); + +protected: + + void init(); + void transformHelper(); + void transform(); + void finalize(); + + vmime_uint32 m_hash[4]; + + unsigned long m_byteCount; + vmime_uint8 m_block[64]; + + bool m_finalized; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_MD5_HPP_INCLUDED diff --git a/src/utility/path.cpp b/src/utility/path.cpp new file mode 100644 index 00000000..477f29dd --- /dev/null +++ b/src/utility/path.cpp @@ -0,0 +1,196 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "path.hpp" + +#include + + +namespace vmime { +namespace utility { + + +path::path() +{ +} + + +path::path(const component& c) +{ + m_list.push_back(c); +} + + +path::path(const path& p) +{ + m_list.resize(p.m_list.size()); + std::copy(p.m_list.begin(), p.m_list.end(), m_list.begin()); +} + + +path::path(const string& s) +{ + m_list.push_back(component(s)); +} + + +path path::operator/(const path& p) const +{ + path pr(*this); + pr /= p; + + return (pr); +} + + +path path::operator/(const component& c) const +{ + path pr(*this); + pr /= c; + + return (pr); +} + + +path& path::operator/=(const path& p) +{ + const list::size_type size = m_list.size(); + + m_list.resize(size + p.m_list.size()); + std::copy(p.m_list.begin(), p.m_list.end(), m_list.begin() + size); + + return (*this); +} + + +path& path::operator/=(const component& c) +{ + m_list.push_back(c); + return (*this); +} + + +path path::parent() const +{ + path p; + + if (!empty()) + { + p.m_list.resize(m_list.size() - 1); + std::copy(m_list.begin(), m_list.end() - 1, p.m_list.begin()); + } + + return (p); +} + + +path& path::operator=(const path& p) +{ + m_list.resize(p.m_list.size()); + std::copy(p.m_list.begin(), p.m_list.end(), m_list.begin()); + + return (*this); +} + + +path& path::operator=(const component& c) +{ + m_list.resize(1); + m_list[0] = c; + + return (*this); +} + + +const bool path::operator==(const path& p) const +{ + if (m_list.size() != p.m_list.size()) + return (false); + + list::const_iterator i = m_list.begin(); + list::const_iterator j = p.m_list.begin(); + + bool equal = true; + + for ( ; equal && i != m_list.end() ; ++i, ++j) + //equal = (*i == *j); + equal = ((*i).buffer() == (*j).buffer()); + + return (equal); +} + + +const bool path::operator!=(const path& p) const +{ + return (!(*this == p)); +} + + +const bool path::empty() const +{ + return (m_list.empty()); +} + + +const path::component path::last() const +{ + return (empty() ? component("") : m_list[m_list.size() - 1]); +} + + +path::component& path::last() +{ + return (m_list[m_list.size() - 1]); +} + + +const int path::size() const +{ + return (m_list.size()); +} + + +const path::component& path::operator[](const int x) const +{ + return (m_list[x]); +} + + +path::component& path::operator[](const int x) +{ + return (m_list[x]); +} + + +const bool path::isDirectParentOf(const path& p) const +{ + if (p.size() != size() + 1) + return (false); + + bool equal = true; + + for (int i = 0 ; equal && i < size() ; ++i) + equal = (m_list[i] == p.m_list[i]); + + return (equal); +} + + +} // utility +} // vmime diff --git a/src/utility/path.hpp b/src/utility/path.hpp new file mode 100644 index 00000000..bc980889 --- /dev/null +++ b/src/utility/path.hpp @@ -0,0 +1,124 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_PATH_HPP_INCLUDED +#define VMIME_UTILITY_PATH_HPP_INCLUDED + + +#include + +#include "../types.hpp" +#include "../word.hpp" + + +namespace vmime { +namespace utility { + + +/** Abstract representation of a path (filesystem, mailbox, etc). + */ + +class path +{ +public: + + typedef vmime::word component; + typedef std::vector list; + + // Construct a path + path(); + path(const component& c); + path(const path& p); + path(const string& s); + + // Append a component to a path + path operator/(const path& p) const; + path operator/(const component& c) const; + + path& operator/=(const path& p); + path& operator/=(const component& c); + + // Return the parent path + path parent() const; + + // Assignment + path& operator=(const path& p); + path& operator=(const component& c); + + // Path comparison + const bool operator==(const path& p) const; + const bool operator!=(const path& p) const; + + /** Test whether this path is empty (root). + * + * @return true if the path is empty (no components = root) + */ + const bool empty() const; + + /** Return the last component of this path (const version). + * + * @return last component + */ + const component last() const; + + /** Return the last component of this path (non-const version). + * + * @return last component + */ + component& last(); + + /** Return the number of components in this path. + * + * @return number of components + */ + const int size() const; + + /** Return the specified component of the path (const version). + * + * @param x index of the component + * @return component at the specified index + */ + const component& operator[](const int x) const; + + /** Return the specified component of the path (non-const version). + * + * @param x index of the component + * @return component at the specified index + */ + component& operator[](const int x); + + /** Test whether this path is a direct parent of another one. + * + * @param p other path + * @return true if the specified path is a child (direct or + * indirect) of this path, false otherwise + */ + const bool isDirectParentOf(const path& p) const; + +private: + + list m_list; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_PATH_HPP_INCLUDED diff --git a/src/utility/random.cpp b/src/utility/random.cpp new file mode 100644 index 00000000..6896d83c --- /dev/null +++ b/src/utility/random.cpp @@ -0,0 +1,59 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "random.hpp" +#include "platformDependant.hpp" + +#include + + +namespace vmime { +namespace utility { + + +unsigned int random::m_next(static_cast(::std::time(NULL))); + + +const unsigned int random::next() +{ + // Park and Miller's minimal standard generator: + // xn+1 = (a * xn + b) mod c + // xn+1 = (16807 * xn) mod (2^31 - 1) + static const unsigned long a = 16807; + static const unsigned long c = (1 << ((sizeof(int) << 3) - 1)); + + m_next = static_cast((a * m_next) % c); + return (m_next); +} + + +const unsigned int random::time() +{ + return (platformDependant::getHandler()->getUnixTime()); +} + + +const unsigned int random::process() +{ + return (platformDependant::getHandler()->getProcessId()); +} + + +} // utility +} // vmime diff --git a/src/utility/random.hpp b/src/utility/random.hpp new file mode 100644 index 00000000..0d9dd92a --- /dev/null +++ b/src/utility/random.hpp @@ -0,0 +1,62 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_RANDOM_HPP_INCLUDED +#define VMIME_UTILITY_RANDOM_HPP_INCLUDED + + +namespace vmime { +namespace utility { + + +class random +{ +public: + + /** Return a new random number. + * + * @return random number + */ + static const unsigned int next(); + + /** Return the current time as a number (may be used to + * build "random" strings). + * + * @return time as a number + */ + static const unsigned int time(); + + /** Return the current process number (may be user to + * build "random" strings). + * + * @return process number + */ + static const unsigned int process(); + +protected: + + static unsigned int m_next; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_RANDOM_HPP_INCLUDED diff --git a/src/utility/singleton.cpp b/src/utility/singleton.cpp new file mode 100644 index 00000000..c960a64a --- /dev/null +++ b/src/utility/singleton.cpp @@ -0,0 +1,53 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "singleton.hpp" + + +namespace vmime { +namespace utility { + + +singletonManager::singletonManager() +{ +} + + +singletonManager::~singletonManager() +{ + for (std::list ::iterator it = m_list.begin() ; it != m_list.end() ; ++it) + delete (*it); +} + + +singletonManager* singletonManager::getInstance() +{ + static singletonManager inst; + return (&inst); +} + + +void singletonManager::manage(abstractSingleton* s) +{ + m_list.push_back(s); +} + + +} // utility +} // vmime diff --git a/src/utility/singleton.hpp b/src/utility/singleton.hpp new file mode 100644 index 00000000..33def75b --- /dev/null +++ b/src/utility/singleton.hpp @@ -0,0 +1,92 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_SINGLETON_HPP_INCLUDED +#define VMIME_UTILITY_SINGLETON_HPP_INCLUDED + + +#include + + +namespace vmime { +namespace utility { + + +// Singleton abstract base class. + +class abstractSingleton +{ + friend class singletonManager; + +protected: + + abstractSingleton() { } + virtual ~abstractSingleton() { } +}; + + +// Singleton manager +// (for automatic clean-up of all instanciated singletons). + +class singletonManager +{ +public: + + static singletonManager* getInstance(); + + void manage(abstractSingleton* s); + +private: + + singletonManager(); + ~singletonManager(); + + std::list m_list; +}; + + +// A singleton template. + +template +class singleton : public abstractSingleton +{ +protected: + + singleton() { } + ~singleton() { } + +public: + + static TYPE* getInstance() + { + static TYPE* inst = NULL; + + if (!inst) + singletonManager::getInstance()->manage(inst = new TYPE()); + + return (inst); + } +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_SINGLETON_HPP_INCLUDED diff --git a/src/utility/smartPtr.hpp b/src/utility/smartPtr.hpp new file mode 100644 index 00000000..9905ae2f --- /dev/null +++ b/src/utility/smartPtr.hpp @@ -0,0 +1,166 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_SMARTPTR_HPP_INCLUDED +#define VMIME_UTILITY_SMARTPTR_HPP_INCLUDED + + +namespace vmime { +namespace utility { + + +/** Simple auto-delete pointer. + */ + +template +class auto_ptr +{ +private: + + T* const m_ptr; + +public: + + auto_ptr(T* const ptr) : m_ptr(ptr) { } + ~auto_ptr() { delete (m_ptr); } + + operator T*() { return (m_ptr); } + + T* const operator ->() { return (m_ptr); } + T& operator *() { return (*m_ptr); } +}; + + +/** Smart auto-delete, referencable and copiable pointer. + */ + +template +class smart_ptr +{ +private: + + struct data + { + int refCount; + T* ptr; + }; + + data* m_data; + + + typedef std::map MapType; + static MapType sm_map; + +public: + + smart_ptr() : m_data(NULL) { } + smart_ptr(T* const ptr) : m_data(NULL) { if (ptr) { attach(ptr); } } + smart_ptr(smart_ptr& ptr) : m_data(NULL) { if (ptr.m_data) { attach(ptr); } } + + ~smart_ptr() { detach(); } + + smart_ptr& operator=(smart_ptr& ptr) + { + attach(ptr); + return (*this); + } + + smart_ptr& operator=(T* const ptr) + { + if (!ptr) + detach(); + else + attach(ptr); + + return (*this); + } + + operator T*() { return (m_data ? m_data->ptr : NULL); } + operator const T*() { return (m_data ? m_data->ptr : NULL); } + + T& operator *() { return (*(m_data->ptr)); } + T* operator ->() { return (m_data->ptr); } + + const T* const ptr() const { return (m_data ? m_data->ptr : NULL); } + T* const ptr() { return (m_data ? m_data->ptr : NULL); } + +private: + + void detach() + { + if (m_data) + { + if (m_data->refCount == 1) + { + typename MapType::iterator it = sm_map.find(m_data->ptr); + if (it != sm_map.end()) sm_map.erase(it); + + delete (m_data->ptr); + delete (m_data); + } + else + { + m_data->refCount--; + } + + m_data = NULL; + } + } + + void attach(T* const ptr) + { + detach(); + + typename MapType::iterator it = sm_map.find(ptr); + + if (it != sm_map.end()) + { + (*it).second->refCount++; + } + else + { + m_data = new data; + m_data->refCount = 1; + m_data->ptr = ptr; + + sm_map.insert(typename MapType::value_type(ptr, m_data)); + } + } + + void attach(smart_ptr & ptr) + { + data* newData = ptr.m_data; + if (newData) newData->refCount++; + + detach(); + + m_data = newData; + } +}; + + +template +typename smart_ptr ::MapType smart_ptr ::sm_map; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_SMARTPTR_HPP_INCLUDED diff --git a/src/utility/stream.cpp b/src/utility/stream.cpp new file mode 100644 index 00000000..06d4ba27 --- /dev/null +++ b/src/utility/stream.cpp @@ -0,0 +1,257 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "stream.hpp" +#include "stringProxy.hpp" + +#include // for std::copy +#include // for std::back_inserter + + +namespace vmime { +namespace utility { + + +// Helpers + +outputStream& operator<<(outputStream& os, const stream::value_type c) +{ + os.write(&c, 1); + return (os); +} + + +outputStream& operator<<(outputStream& os, const string& str) +{ + os.write(str.data(), str.length()); + return (os); +} + + +const stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os) +{ + stream::value_type buffer[65536]; + stream::size_type total = 0; + + while (!is.eof()) + { + const stream::size_type read = is.read(buffer, sizeof(buffer)); + + if (read != 0) + { + os.write(buffer, read); + total += read; + } + } + + return (total); +} + + + +// outputStreamAdapter + +outputStreamAdapter::outputStreamAdapter(std::ostream& os) + : m_stream(os) +{ +} + + +void outputStreamAdapter::write + (const value_type* const data, const size_type count) +{ + m_stream.write(data, count); +} + + + +// outputStreamStringAdapter + +outputStreamStringAdapter::outputStreamStringAdapter(string& buffer) + : m_buffer(buffer) +{ + m_buffer.clear(); +} + + +void outputStreamStringAdapter::write(const value_type* const data, const size_type count) +{ + // TODO: better way? + std::copy(data, data + count, std::back_inserter(m_buffer)); +} + + + +// inputStreamAdapter + +inputStreamAdapter::inputStreamAdapter(std::istream& is) + : m_stream(is) +{ +} + + +const bool inputStreamAdapter::eof() const +{ + return (m_stream.eof()); +} + + +void inputStreamAdapter::reset() +{ + m_stream.seekg(0, std::ios::beg); + m_stream.clear(); +} + + +const stream::size_type inputStreamAdapter::read + (value_type* const data, const size_type count) +{ + m_stream.read(data, count); + return (m_stream.gcount()); +} + + + +// inputStreamStringAdapter + +inputStreamStringAdapter::inputStreamStringAdapter(const string& buffer) + : m_buffer(buffer), m_begin(0), m_end(buffer.length()), m_pos(0) +{ +} + + +inputStreamStringAdapter::inputStreamStringAdapter(const string& buffer, + const string::size_type begin, const string::size_type end) + : m_buffer(buffer), m_begin(begin), m_end(end), m_pos(begin) +{ +} + + +const bool inputStreamStringAdapter::eof() const +{ + return (m_pos >= m_end); +} + + +void inputStreamStringAdapter::reset() +{ + m_pos = m_begin; +} + + +const stream::size_type inputStreamStringAdapter::read + (value_type* const data, const size_type count) +{ + if (m_pos + count >= m_end) + { + const size_type remaining = m_end - m_pos; + + std::copy(m_buffer.begin() + m_pos, m_buffer.end(), data); + m_pos = m_end; + return (remaining); + } + else + { + std::copy(m_buffer.begin() + m_pos, m_buffer.begin() + m_pos + count, data); + m_pos += count; + return (count); + } +} + + + +// inputStreamStringProxyAdapter + +inputStreamStringProxyAdapter::inputStreamStringProxyAdapter(const stringProxy& buffer) + : m_buffer(buffer), m_pos(0) +{ +} + + +const bool inputStreamStringProxyAdapter::eof() const +{ + return (m_pos >= m_buffer.length()); +} + + +void inputStreamStringProxyAdapter::reset() +{ + m_pos = 0; +} + + +const stream::size_type inputStreamStringProxyAdapter::read + (value_type* const data, const size_type count) +{ + const size_type remaining = m_buffer.length() - m_pos; + + if (count > remaining) + { + std::copy(m_buffer.it_begin() + m_pos, m_buffer.it_end(), data); + m_pos = m_buffer.length(); + return (remaining); + } + else + { + std::copy(m_buffer.it_begin() + m_pos, m_buffer.it_begin() + m_pos + count, data); + m_pos += count; + return (count); + } +} + + + +// inputStreamPointerAdapter + +inputStreamPointerAdapter::inputStreamPointerAdapter(std::istream* is, const bool own) + : m_stream(is), m_own(own) +{ +} + + +inputStreamPointerAdapter::~inputStreamPointerAdapter() +{ + if (m_own) + delete (m_stream); +} + + +const bool inputStreamPointerAdapter::eof() const +{ + return (m_stream->eof()); +} + + +void inputStreamPointerAdapter::reset() +{ + m_stream->seekg(0, std::ios::beg); + m_stream->clear(); +} + + +const stream::size_type inputStreamPointerAdapter::read + (value_type* const data, const size_type count) +{ + m_stream->read(data, count); + return (m_stream->gcount()); +} + + +} // utility +} // vmime diff --git a/src/utility/stream.hpp b/src/utility/stream.hpp new file mode 100644 index 00000000..8f8de54c --- /dev/null +++ b/src/utility/stream.hpp @@ -0,0 +1,263 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_STREAM_HPP_INCLUDED +#define VMIME_UTILITY_STREAM_HPP_INCLUDED + + +#include +#include + +#include "../types.hpp" + + +namespace vmime { +namespace utility { + + +class stringProxy; + + +/** Base class for input/output stream. + */ + +class stream +{ +public: + + virtual ~stream() { } + + /** Type used to read/write one byte in the stream. + */ + typedef string::value_type value_type; + + /** Type used for lengths in streams. + */ + typedef string::size_type size_type; +}; + + + +/** Simple output stream. + */ + +class outputStream : public stream +{ +public: + + /** Write data to the stream. + * + * @param data buffer containing data to write + * @param count number of bytes to write + */ + virtual void write(const value_type* const data, const size_type count) = 0; +}; + + + +/** Simple input stream. + */ + +class inputStream : public stream +{ +public: + + /** Test for end of stream (no more data to read). + * + * @return true if we have reached the end of stream, false otherwise + */ + virtual const bool eof() const = 0; + + /** Set the read pointer to the beginning of the stream. + * + * @warning WARNING: this may not work for all stream types. + */ + virtual void reset() = 0; + + /** Read data from the stream. + * + * @param data will receive the data read + * @param count maximum number of bytes to read + * @return number of bytes read + */ + virtual const size_type read(value_type* const data, const size_type count) = 0; +}; + + + +// Helpers functions + +outputStream& operator<<(outputStream& os, const string& str); +outputStream& operator<<(outputStream& os, const stream::value_type c); + + +template +outputStream& operator<<(outputStream& os, const char (&str)[N]) +{ + os.write(str, N - 1); + return (os); +} + + +/** Copy data from one stream into another stream using a buffered method. + * + * @param is input stream (source data) + * @param os output stream (destination for data) + * @return number of bytes copied + */ + +const stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os); + + + +// Adapters + + +/** An adapter class for C++ standard output streams. + */ + +class outputStreamAdapter : public outputStream +{ +public: + + /** @param os output stream to wrap + */ + outputStreamAdapter(std::ostream& os); + + void write(const value_type* const data, const size_type count); + +private: + + std::ostream& m_stream; +}; + + +/** An adapter class for string output. + */ + +class outputStreamStringAdapter : public outputStream +{ +public: + + outputStreamStringAdapter(string& buffer); + + void write(const value_type* const data, const size_type count); + +private: + + string& m_buffer; +}; + + +/** An adapter class for C++ standard input streams. + */ + +class inputStreamAdapter : public inputStream +{ +public: + + /** @param is input stream to wrap + */ + inputStreamAdapter(std::istream& is); + + const bool eof() const; + void reset(); + const size_type read(value_type* const data, const size_type count); + +private: + + std::istream& m_stream; +}; + + +/** An adapter class for string input. + */ + +class inputStreamStringAdapter : public inputStream +{ +public: + + inputStreamStringAdapter(const string& buffer); + inputStreamStringAdapter(const string& buffer, const string::size_type begin, const string::size_type end); + + const bool eof() const; + void reset(); + const size_type read(value_type* const data, const size_type count); + +private: + + const string m_buffer; // do _NOT_ keep a reference... + const string::size_type m_begin; + const string::size_type m_end; + string::size_type m_pos; +}; + + +/** An adapter class for stringProxy input. + */ + +class inputStreamStringProxyAdapter : public inputStream +{ +public: + + /** @param buffer stringProxy object to wrap + */ + inputStreamStringProxyAdapter(const stringProxy& buffer); + + const bool eof() const; + void reset(); + const size_type read(value_type* const data, const size_type count); + +private: + + const stringProxy& m_buffer; + string::size_type m_pos; +}; + + +/** An adapter class for pointer to C++ standard input stream. + */ + +class inputStreamPointerAdapter : public inputStream +{ +public: + + /** @param is input stream to wrap + * @param own if set to 'true', the pointer will be deleted when + * this object is destroyed + */ + inputStreamPointerAdapter(std::istream* is, const bool own = true); + ~inputStreamPointerAdapter(); + + const bool eof() const; + void reset(); + const size_type read(value_type* const data, const size_type count); + +private: + + std::istream* m_stream; + const bool m_own; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_STREAM_HPP_INCLUDED diff --git a/src/utility/stringProxy.cpp b/src/utility/stringProxy.cpp new file mode 100644 index 00000000..f79e8a58 --- /dev/null +++ b/src/utility/stringProxy.cpp @@ -0,0 +1,131 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "stringProxy.hpp" + +#include +#include + + +namespace vmime { +namespace utility { + + +stringProxy::stringProxy() + : m_start(0), m_end(0) +{ +} + + +stringProxy::stringProxy(const stringProxy& s) + : m_buffer(s.m_buffer), m_start(s.m_start), m_end(s.m_end) +{ +} + + +stringProxy::stringProxy(const string_type& s, const size_type start, const size_type end) + : m_buffer(s), m_start(start), + m_end(end == std::numeric_limits ::max() ? s.length() : end) +{ +} + + +void stringProxy::set(const string_type& s, const size_type start, const size_type end) +{ + m_buffer = s; + m_start = start; + + if (end == std::numeric_limits ::max()) + m_end = s.length(); + else + m_end = end; +} + + +void stringProxy::detach() +{ + m_buffer.clear(); + m_start = m_end = 0; +} + + +stringProxy& stringProxy::operator=(const stringProxy& s) +{ + m_buffer = s.m_buffer; + m_start = s.m_start; + m_end = s.m_end; + + return (*this); +} + + +stringProxy& stringProxy::operator=(const string_type& s) +{ + m_buffer = s; + m_start = 0; + m_end = s.length(); + + return (*this); +} + + +void stringProxy::extract(outputStream& os, const size_type start, const size_type end) const +{ + if (end == std::numeric_limits ::max()) + os.write(m_buffer.data() + m_start + start, m_end - start - m_start); + else + os.write(m_buffer.data() + m_start + start, end - start - m_start); +} + + +const stringProxy::size_type stringProxy::length() const +{ + return (m_end - m_start); +} + + +const stringProxy::size_type stringProxy::start() const +{ + return (m_start); +} + + +const stringProxy::size_type stringProxy::end() const +{ + return (m_end); +} + + +std::ostream& operator<<(std::ostream& os, const stringProxy& s) +{ + outputStreamAdapter adapter(os); + s.extract(adapter); + return (os); +} + + +outputStream& operator<<(outputStream& os, const stringProxy& s) +{ + s.extract(os); + return (os); +} + + +} // utility +} // vmime diff --git a/src/utility/stringProxy.hpp b/src/utility/stringProxy.hpp new file mode 100644 index 00000000..e8001aed --- /dev/null +++ b/src/utility/stringProxy.hpp @@ -0,0 +1,90 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_UTILITY_STRINGPROXY_HPP_INCLUDED +#define VMIME_UTILITY_STRINGPROXY_HPP_INCLUDED + + +#include + +#include "../types.hpp" +#include "stream.hpp" + + +namespace vmime { +namespace utility { + + +/** This class is a proxy for the string class. This takes + * advantage of the COW (copy-on-write) system that might + * be used in "std::string" implementation. + */ + +class stringProxy +{ +public: + + typedef string::size_type size_type; + typedef string string_type; + + + // Consruction + stringProxy(); + stringProxy(const stringProxy& s); + stringProxy(const string_type& s, const size_type start = 0, const size_type end = std::numeric_limits ::max()); + + // Assignment + void set(const string_type& s, const size_type start = 0, const size_type end = std::numeric_limits ::max()); + void detach(); + + stringProxy& operator=(const stringProxy& s); + stringProxy& operator=(const string_type& s); + + // Extract some portion (or whole) of the string + // and output it into a stream. + void extract(outputStream& os, const size_type start = 0, const size_type end = std::numeric_limits ::max()) const; + + // Return the "virtual" length of the string + const size_type length() const; + + // Return the boundaries of the "virtual" string + const size_type start() const; + const size_type end() const; + + string::const_iterator it_begin() const { return (m_buffer.begin() + m_start); } + string::const_iterator it_end() const { return (m_buffer.begin() + m_end); } + +private: + + string_type m_buffer; + + size_type m_start; + size_type m_end; +}; + + +std::ostream& operator<<(std::ostream& os, const stringProxy& s); +outputStream& operator<<(outputStream& os, const stringProxy& s); + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_STRINGPROXY_HPP_INCLUDED diff --git a/src/vmime b/src/vmime new file mode 100644 index 00000000..4953d8fd --- /dev/null +++ b/src/vmime @@ -0,0 +1,93 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should.have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_INCLUDED +#define VMIME_INCLUDED + + +// Configuration +#include "config.hpp" + +// Base definitions +#include "base.hpp" +#include "exception.hpp" +#include "options.hpp" +#include "platformDependant.hpp" + +// Base components +#include "dateTime.hpp" +#include "message.hpp" +#include "bodyPart.hpp" +#include "charset.hpp" +#include "text.hpp" +#include "encoding.hpp" +#include "disposition.hpp" +#include "mailbox.hpp" +#include "mailboxGroup.hpp" +#include "mailboxList.hpp" +#include "addressList.hpp" +#include "mediaType.hpp" +#include "messageId.hpp" + +// Message components +#include "message.hpp" + +// Header fields +#include "headerFieldFactory.hpp" +#include "mailboxField.hpp" +#include "defaultField.hpp" +#include "addressListField.hpp" +#include "parameterizedHeaderField.hpp" +#include "relayField.hpp" + +// Encoders +#include "encoderFactory.hpp" + +// Message builder/parser +#include "messageBuilder.hpp" +#include "messageParser.hpp" + +#include "fileAttachment.hpp" +#include "defaultAttachment.hpp" + +#include "plainTextPart.hpp" +#include "htmlTextPart.hpp" + +// Property set +#include "propertySet.hpp" + +// Messaging features +#if VMIME_HAVE_MESSAGING_FEATURES + #include "messaging/socket.hpp" + + #include "messaging/service.hpp" + #include "messaging/store.hpp" + #include "messaging/transport.hpp" + + #include "messaging/session.hpp" + #include "messaging/authenticator.hpp" + #include "messaging/defaultAuthenticator.hpp" + #include "messaging/simpleAuthenticator.hpp" + + #include "messaging/folder.hpp" + #include "messaging/message.hpp" +#endif // VMIME_HAVE_MESSAGING_FEATURES + + +#endif // VMIME_INCLUDED diff --git a/src/word.cpp b/src/word.cpp new file mode 100644 index 00000000..6578ec5f --- /dev/null +++ b/src/word.cpp @@ -0,0 +1,102 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "word.hpp" + + +namespace vmime +{ + + +word::word() + : m_charset(charset::getLocaleCharset()) +{ +} + + +word::word(const word& w) + : m_buffer(w.m_buffer), m_charset(w.m_charset) +{ +} + + +word::word(const string& buffer) // Defaults to locale charset + : m_buffer(buffer), m_charset(charset::getLocaleCharset()) +{ +} + + +word::word(const string& buffer, const class charset& charset) + : m_buffer(buffer), m_charset(charset) +{ +} + + +#if VMIME_WIDE_CHAR_SUPPORT + +const wstring word::getDecodedText() const +{ + wstring out; + + charset::decode(m_buffer, out, m_charset); + + return (out); +} + +#endif + + +word& word::operator=(const word& w) +{ + m_buffer = w.m_buffer; + m_charset = w.m_charset; + return (*this); +} + + +word& word::operator=(const string& s) +{ + m_buffer = s; + return (*this); +} + + +const bool word::operator==(const word& w) const +{ + return (m_charset == w.m_charset && m_buffer == w.m_buffer); +} + + +const bool word::operator!=(const word& w) const +{ + return (m_charset != w.m_charset || m_buffer != w.m_buffer); +} + + +const string word::getConvertedText(const class charset& dest) const +{ + string out; + + charset::convert(m_buffer, out, m_charset, dest); + + return (out); +} + + +} // vmime diff --git a/src/word.hpp b/src/word.hpp new file mode 100644 index 00000000..e9321942 --- /dev/null +++ b/src/word.hpp @@ -0,0 +1,74 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef VMIME_WORD_HPP_INCLUDED +#define VMIME_WORD_HPP_INCLUDED + + +#include "charset.hpp" + + +namespace vmime +{ + + +/** A class that encapsulates an encoded-word (RFC-2047): + * some text encoded into one specified charset. + */ + +class word +{ +public: + + word(); + word(const word& w); + word(const string& buffer); // Defaults to locale charset + word(const string& buffer, const class charset& charset); + + const string& buffer() const { return (m_buffer); } + string& buffer() { return (m_buffer); } + + const class charset& charset() const { return (m_charset); } + class charset& charset() { return (m_charset); } + + + word& operator=(const word& w); + word& operator=(const string& s); + + const bool operator==(const word& w) const; + const bool operator!=(const word& w) const; + +#if VMIME_WIDE_CHAR_SUPPORT + const wstring getDecodedText() const; +#endif + const string getConvertedText(const class charset& dest) const; + +protected: + + // The "m_buffer" of this word holds the data, and this data is encoded + // in the specified "m_charset". + string m_buffer; + class charset m_charset; +}; + + +} // vmime + + +#endif // VMIME_WORD_HPP_INCLUDED diff --git a/tests/charset/main.cpp b/tests/charset/main.cpp new file mode 100644 index 00000000..5bf3a647 --- /dev/null +++ b/tests/charset/main.cpp @@ -0,0 +1,40 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include +#include + +#include "../../src/vmime" +#include "../../examples/common.inc" + + +int main(int argc, char* argv[]) +{ + // VMime initialization + vmime::platformDependant::setHandler(); + + + const vmime::string from(argv[1]); + const vmime::string to(argv[2]); + + vmime::inputStreamAdapter in(std::cin); + vmime::outputStreamAdapter out(std::cout); + + vmime::charset::convert(in, out, from, to); +} diff --git a/tests/charset/run-test.sh b/tests/charset/run-test.sh new file mode 100755 index 00000000..d7ad5295 --- /dev/null +++ b/tests/charset/run-test.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +TEST_DIR="./test-suites" +TEMP_DIR="/tmp" +PROGRAM="./main" + + +testFiles=`cd $TEST_DIR ; find . -regex '\./[^\.]*\.in\..*' -maxdepth 1 -type f` + +echo +echo Testing charset conversions +echo ===================================================================== + +for testFile in $testFiles ; do + + testName=`echo $testFile | sed 's/^\.\/\([^\.]*\).*/\1/'` + sourceCharset=`echo $testFile | sed 's/^\.\/[^\.]*\.[^\.]*\.\(.*\)/\1/'` + + testOutFiles=`cd $TEST_DIR ; find . -regex "\./$testName\.out\..*" -maxdepth 1 -type f` + + for testOutFile in $testOutFiles ; do + + destCharset=`echo $testOutFile | sed 's/^\.\/[^\.]*\.[^\.]*\.\(.*\)/\1/'` + + printf %20s "$testName " + printf %30s "$sourceCharset --> $destCharset : " + + $PROGRAM $sourceCharset $destCharset < $TEST_DIR/$testFile > $TEMP_DIR/vmime_result + + diff="diff $TEMP_DIR/vmime_result $TEST_DIR/$testOutFile" + res=`$diff` + + if [ "$res" = "" ] + then + echo "[OK]" + else + diffFile=$TEMP_DIR/vmime.charset.$testName.$sourceCharset.$destCharset.diff + echo "[NO: diff file is $diffFile]" + $diff > $diffFile + fi + + done + +done + +echo + diff --git a/tests/charset/test-suites/gnu.in.utf-8 b/tests/charset/test-suites/gnu.in.utf-8 new file mode 100644 index 00000000..782bb84e --- /dev/null +++ b/tests/charset/test-suites/gnu.in.utf-8 @@ -0,0 +1,48 @@ +Korean Part. +GNU 프로ì íŠ¸ì˜ ì›¹ì„œë²„ì¸ www.gnu.orgì— ì˜¤ì‹  ê²ƒì„ í™˜ì˜í•©ë‹ˆë‹¤. GNU 프로ì íŠ¸ëŠ” +GNU 시스템ì´ë¼ê³  불리는 유닉스 í˜•íƒœì˜ ì™„ë²½í•œ ìžìœ  소프트웨어 ìš´ì˜ì²´ì œë¥¼ +개발하기 위해서 1984년부터 시작ë˜ì—ˆìŠµë‹ˆë‹¤. (GNUë¼ëŠ” 단어는 ``GNU's Not +Unix''를 ì˜ë¯¸í•˜ëŠ” ìž¬ê·€ì  ì•½ì–´ì´ë©° ``ê·¸-뉴"ë¼ê³  ë°œìŒí•©ë‹ˆë‹¤.) GNU +시스템으로부터 연유한 다양한 ì¢…ë¥˜ì˜ ìš´ì˜ì²´ì œë“¤ì´ 현재 ``리눅스''ë¼ëŠ” +ì´ë¦„으로 사용ë˜ê³  있지만, 시스템 커ë„ë¡œ 리눅스를 탑재한 ìš´ì˜ì²´ì œì˜ 보다 +정확한 ì´ë¦„ì€ GNU/리눅스 시스템입니다. + +Japanese Part + +GNU プロジェクトã®ã‚¦ã‚§ãƒ–サーãƒã€www.gnu.org ã¸ã‚ˆã†ã“ã。1984 å¹´ã®ãƒ—ロジェクト +開始以æ¥ã€GNU プロジェクトã§ã¯ã€Unix ã«ä¼¼ãŸ フリーソフトウェアã®å®Œå…¨ãªã‚ªãƒšãƒ¬ãƒ¼ +ティングシステムã€GNU システムを開発ã—ã¦æ¥ã¾ã—ãŸ(GNU ã¨ã¯ã€ŒGNU's Not Unix(GNU +㯠Unix ã§ã¯ãªã„)ã€ã®å†å¸°é ­å­—語ã§ã‚ã‚Šã€ã€Œã‚°ãƒ‹ãƒ¥ãƒ¼ã€ã¨ç™ºéŸ³ã•ã‚Œã¾ã™)。ç¾åœ¨ã€ã‚«ãƒ¼ãƒ +ルã¨ã—㦠Linux を用ã„㟠GNU システムã®ã•ã¾ã–ã¾ãªå¤‰ç¨®ãŒåºƒã使ã‚ã‚Œã¦ã„ã¾ã™ã€‚ã“ã‚Œ +らã®ã‚·ã‚¹ãƒ†ãƒ ã¯ã€ŒLinuxã€ã¨å‘¼ã°ã‚Œã‚‹ã“ã¨ãŒå¤šã„ã®ã§ã™ãŒã€ã‚ˆã‚Šæ­£ç¢ºã«ã¯ GNU/Linux +システム ã¨å‘¼ã°ã‚Œã‚‹ã‚‚ã®ãªã®ã§ã™ã€‚ + +Chinese Part. + +歡迎來到GNU 專案的伺æœä¸»æ©Ÿï¼Œwww.gnu.org。 GNU 專案 開始於1984年,旨在發展一 +個 Unix-like 且為 自由軟體 的作業系統: GNU 系統。(GNU 是由``GNU's Not +Unix''所éžè¿´å®šç¾©å‡ºçš„頭字語);它的發音為"guh-NEW"。å„種使用 Linux 作為核心的 GNU +作業系統正被廣泛的使用著;雖然這些系統通常被稱作 ``Linux'',但是它們應該更精 +確地被稱為 GNU/Linux 系統。 + +English Part. + +Welcome to the GNU Project web server, www.gnu.org. The GNU Project was +launched in 1984 to develop a complete Unix-like operating system which is +free software: the GNU system. (GNU is a recursive acronym for ``GNU's Not +Unix''; it is pronounced "guh-NEW".) Variants of the GNU operating system, +which use the kernel Linux, are now widely used; though these systems are +often referred to as ``Linux'', they are more accurately called GNU/Linux +systems. + +French Part. +Bienvenue sur le serveur web du projet GNU, www.gnu.org. Le projet GNU a été +lancé en 1984 afin de développer un système d'exploitation complet, +semblable à Unix et qui soit un logiciel libre: le système GNU. (« GNU » +est l'acronyme récursif the « GNU's Not Unix »; on le prononce « gnou » +avec un G audible) Des variantes du système d'exploitation GNU, basées sur +le noyau « Linux », sont utilisées largement à présent; bien que ces +systèmes soient communément appelés par le terme « Linux », ils le +seraient plus exactement par « GNU/Linux ». + + diff --git a/tests/charset/test-suites/gnu.out.iso-8859-1 b/tests/charset/test-suites/gnu.out.iso-8859-1 new file mode 100644 index 00000000..bc11399e --- /dev/null +++ b/tests/charset/test-suites/gnu.out.iso-8859-1 @@ -0,0 +1,48 @@ +Korean Part. +GNU ??????????????? ???????????? www.gnu.org??? ?????? ?????? ???????????????. GNU ??????????????? +GNU ?????????????????? ????????? ????????? ????????? ????????? ?????? ??????????????? ??????????????? +???????????? ????????? 1984????????? ?????????????????????. (GNU?????? ????????? ``GNU's Not +Unix''??? ???????????? ????????? ???????????? ``???-???"?????? ???????????????.) GNU +????????????????????? ????????? ????????? ????????? ?????????????????? ?????? ``?????????''?????? +???????????? ???????????? ?????????, ????????? ????????? ???????????? ????????? ??????????????? ?????? +????????? ????????? GNU/????????? ??????????????????. + +Japanese Part + +GNU ??????????????????????????????????????????www.gnu.org ??????????????????1984 ???????????????????????? +???????????????GNU ???????????????????????????Unix ????????? ??????????????????????????????????????????????????? +???????????????????????????GNU ???????????????????????????????????????(GNU ?????????GNU's Not Unix(GNU +??? Unix ????????????)????????????????????????????????????????????????????????????????????????)????????????????????? +???????????? Linux ???????????? GNU ??????????????????????????????????????????????????????????????????????????? +????????????????????????Linux?????????????????????????????????????????????????????????????????? GNU/Linux +???????????? ???????????????????????????????????? + +Chinese Part. + +????????????GNU ????????????????????????www.gnu.org??? GNU ?????? ?????????1984????????????????????? +??? Unix-like ?????? ???????????? ?????????????????? GNU ????????????GNU ??????``GNU's Not +Unix''???????????????????????????????????????????????????"guh-NEW"??????????????? Linux ??????????????? GNU +???????????????????????????????????????????????????????????????????????? ``Linux''??????????????????????????? +??????????????? GNU/Linux ????????? + +English Part. + +Welcome to the GNU Project web server, www.gnu.org. The GNU Project was +launched in 1984 to develop a complete Unix-like operating system which is +free software: the GNU system. (GNU is a recursive acronym for ``GNU's Not +Unix''; it is pronounced "guh-NEW".) Variants of the GNU operating system, +which use the kernel Linux, are now widely used; though these systems are +often referred to as ``Linux'', they are more accurately called GNU/Linux +systems. + +French Part. +Bienvenue sur le serveur web du projet GNU, www.gnu.org. Le projet GNU a été +lancé en 1984 afin de développer un système d'exploitation complet, +semblable à Unix et qui soit un logiciel libre: le système GNU. (« GNU » +est l'acronyme récursif the « GNU's Not Unix »; on le prononce « gnou » +avec un G audible) Des variantes du système d'exploitation GNU, basées sur +le noyau « Linux », sont utilisées largement à présent; bien que ces +systèmes soient communément appelés par le terme « Linux », ils le +seraient plus exactement par « GNU/Linux ». + + diff --git a/tests/encoding/main.cpp b/tests/encoding/main.cpp new file mode 100644 index 00000000..78664f8a --- /dev/null +++ b/tests/encoding/main.cpp @@ -0,0 +1,49 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include +#include + +#include "../../src/vmime" +#include "../../examples/common.inc" + + +int main(int argc, char* argv[]) +{ + // VMime initialization + vmime::platformDependant::setHandler(); + + + const vmime::string encoding(argv[1]); + const vmime::string mode(argv[2]); + + vmime::encoder* p = vmime::encoderFactory::getInstance()->create(encoding); + + p->properties()["maxlinelength"] = 76; + + vmime::inputStreamAdapter in(std::cin); + vmime::outputStreamAdapter out(std::cout); + + if (mode == "e") + p->encode(in, out); + else + p->decode(in, out); + + delete (p); +} diff --git a/tests/encoding/run-test.sh b/tests/encoding/run-test.sh new file mode 100755 index 00000000..181f1daf --- /dev/null +++ b/tests/encoding/run-test.sh @@ -0,0 +1,118 @@ +#/bin/sh + +TEST_DIR="./test-suites" +TEMP_DIR="/tmp" +ENCODINGS="base64 quoted-printable uuencode" +PROGRAM="./main" + + +for encoding in $ENCODINGS ; do + + echo + echo Testing encoding \'$encoding\' + echo ===================================================================== + + + ############ + # Encode # + ############ + + echo ENCODE + + testFiles=`cd $TEST_DIR/encode ; find . -regex '\./[^\.]*' -maxdepth 1 -type f | tr -d ./` + + for testFile in $testFiles ; do + + if [ -e $TEST_DIR/encode/$testFile.$encoding ] + then + + printf %20s "$testFile : " + + $PROGRAM $encoding e < $TEST_DIR/encode/$testFile > $TEMP_DIR/vmime_result + + diff="diff $TEMP_DIR/vmime_result $TEST_DIR/encode/$testFile.$encoding" + res=`$diff` + + if [ "$res" = "" ] + then + echo "[OK]" + else + diffFile=$TEMP_DIR/vmime.encode.$encoding.$testFile.diff + echo "[NO: diff file is $diffFile]" + $diff > $diffFile + fi + + fi + + done + + + ############ + # Decode # + ############ + + echo DECODE [1/2] + + for testFile in $testFiles ; do + + if [ -e $TEST_DIR/encode/$testFile.$encoding ] + then + + printf %20s "$testFile : " + + $PROGRAM $encoding d < $TEST_DIR/encode/$testFile.$encoding > $TEMP_DIR/vmime_result + + diff="diff $TEMP_DIR/vmime_result $TEST_DIR/encode/$testFile" + res=`$diff` + + if [ "$res" = "" ] + then + echo "[OK]" + else + diffFile=$TEMP_DIR/vmime.decode.$encoding.$testFile.diff + echo "[NO: diff file is $diffFile]" + $diff > $diffFile + fi + + fi + + done + + + ########################################### + # Decode from data not encoded by VMime # + ########################################### + + echo DECODE [2/2] + + testFiles=`cd $TEST_DIR/decode ; find . -regex '\./[^\.]*' -maxdepth 1 -type f | tr -d ./` + + for testFile in $testFiles ; do + + if [ -e $TEST_DIR/decode/$testFile.$encoding ] + then + + printf %20s "$testFile : " + + $PROGRAM $encoding d < $TEST_DIR/decode/$testFile.$encoding > $TEMP_DIR/vmime_result + + diff="diff $TEMP_DIR/vmime_result $TEST_DIR/decode/$testFile" + res=`$diff` + + if [ "$res" = "" ] + then + echo "[OK]" + else + diffFile=$TEMP_DIR/vmime.decode2.$encoding.$testFile.diff + echo "[NO: diff file is $diffFile]" + $diff > $diffFile + fi + + fi + + done + +done + +echo + diff --git a/tests/encoding/test-suites/decode/ls b/tests/encoding/test-suites/decode/ls new file mode 100644 index 00000000..7100c7c9 Binary files /dev/null and b/tests/encoding/test-suites/decode/ls differ diff --git a/tests/encoding/test-suites/decode/ls.base64 b/tests/encoding/test-suites/decode/ls.base64 new file mode 100644 index 00000000..5d655efc --- /dev/null +++ b/tests/encoding/test-suites/decode/ls.base64 @@ -0,0 +1,1416 @@ + + + + + + + +f +0VM RgEBAQAA AAAAAAAAA +AIAAwABAAAAYJkECDQAAAAkFwEAAAAAADQAIAAHACgAGQAYAAYAAAA0 +AAAAN IAECDSABAjgAAAA4AAAAAUAAAAEAAAAAwAAABQBAAAUgQQIFIEECBMAAAATAAAABAAAAAEA +AAABAAAAAAAAAACABAgAgAQISBIBAEgSAQAFAAAAABAAAAEAAABgEgEAYKIFCGCiBQjkAwAAkAcA +AAYAAAAAEAAAAgAAAOQTAQDkowUI5KMFCNgAAADYAAAABgAAAAQAAAAEAAAAKAEAACiBBAgogQQI +IAAAACAAAAAEAAAABAAAAFDldGQcEgEAHJIFCBySBQgsAAAALAAAAAQAAAAEAAAAL2xpYi9sZC1s +aW51eC5zby4 yAAAEAAAAEAAAAAEAAABHTlUAAAAAAAIAAAACAAAAAAAAAGEAAABnAAAAAAAAADwA +AAA7AAAAMQAAAAAAAAAAAAAAAAAAAFYAAAA+AAAALQAAAAAAAAAfAAAASgAAACsAAAA9AAAAAAAA +AC8AAAAjAAAAIQAAAEkAAABXAAAAOQAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAUAAAAEUAAABeAAAA +AAAAAFsAAAAPAAAA +XQAAAGEAAAAAAAAACwAAABwAAABfAAAAFgAAAAAAAAAqAAAAAAAAACcAAAAA +AAA +ACgAAAFwAAAAAAAAATgAA +AFoAAAAZAAAABwAAAAEAAAA +AAAAAAAAAAAAAAABlAAAANgAAAEwA +AAAAAAAAAAAAAFkAAAAAAAAAAAAAAEMAAABYAAAAYgAAAEIAAABPAAAAAAAAAGAAAABRAAAAKAAA + AAAA AAAaAAAAAAAAACQAAAAA AAAAUgAAADUA AAAlA +A AA IAAA AEYAAAAbA AAAAAAAAAAAAAAEAAAA +YwAAAAAAAABmAAAAOAAAAGQAAAAAAAAACAAAAEQAAABLAAAAVQAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEA +AAASAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAA +A +A +A +A +AAAAAAAAAAAAAAA +AAAAAAAATAAAAAAAA +ABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAB0AAAAsAAAA +AAAAACYAAAAAAAAAAAAAAAAAAAAiAAAAFwAAAA0AAAAAAAAAAwAAAAAAAAAMAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAANwAAAAAAAAAAAAAAPwAAAAAAAAApAAAAAAAAAAAAAAAuAAAASAAAADoA +AAAeAAAACQAAAAAAAAAAAAAANAAAAAYAAAAFAAAAFQAAAFMAAAAYAAAAAAAAAE0AAAAAAAAAAAAA +AEcAAAAAAAAAQAAAAFQAAAAAAAAAAA +AAADIAAAAAAAAAQQAAAAAAAAAAAAAADgAAADMAAAAAAAAA +AAAAAAAAAAAAAAAAdgEAANiTBAg8AAAAEgAAAHwDAADokwQIJAEAABIAAAAzAQAA+JMECCoAAAAS +AAAASQAAAAiUBAg6AAAAEgAAAJkCAAAYlAQIUwAAABIAAAD1AAAAKJQECPEAAAASAAAA1AMAADiU +BAhnAQAAEgAAAH8BAABIlAQIZgAAABIAAABiAAAA5KMFCAAAAAARAPH/6wEAAFiUBAhxAAAAEgAA +AP8BAABolAQIkAAAABIAAAAVAgAAeJQECHwAAAASAAAAugIAAIiUBAg4AAAAEgAAADoCAACYlAQI +nAAAABIAAADhAgAAqJQECD8AAAASAAAAMgIAALiUBAgjAAAAEgAAAEcBAADIlAQI9gAAABIAAABJ +AgAA2JQECEwAAAASAAAA0wIAAOiUBAhbAQAAEgAAAEwDAAD4lAQIowAAABIAAAATAwAACJUECP8F +AAASAAAA/QMAABiVBAhFAAAAEgAAAOgCAAAolQQIJAEAABIAAABcAwAAOJUECDkAAAASAAAAPgEA +AEiVBAgkAQAAEgAAAMMBAABYlQQI/QQAABIAAADUAQAAaJUECDoAAAASAAAAjAAAALCTBAgAAAAA +EgAKALwAAAB4lQQIfQAAABIAAACSAQAAiJUECLUBAAASAAAAQwMAAJiVBAhHIAAAEgAAAB8BAACo +lQQIiwAAABIAAADuAAAAYKYFCAQAAAARABcAHQMAAGSmBQgEAAAAEQAXAM4BAAC4lQQIDQIAABIA +AADFAAAAyJUECI0AAAASAAAAWgIAANiVBAjwAQAAEgAAAG0DAADolQQIJwAAABIAAAAVAAAA+JUE +CEAAAAASAAAAzQMAAAiWBAgbAAAAEgAAALkBAAAYlgQISgAAABIAAABiAgAAKJYECAoAAAASAAAA +/wAAADiWBAiSCAAAEgAAAFcB + + + +AABIlgQIhgYAABIAAABpAQAAWJYECHUBAAASAAAAigEAAGiWBAgO +AgAAEgAAALADAAB4lgQIoAMAABIAAABOAQAAiJYECNMAAAASAAAAcAIAAJiWBAjAAAAAEgAAAKMC +AAColgQIEAEAABIAAAARBAAARKYFCAAAAAAQAPH/uwMAALiWBAgCAQAAEgAAAO8DAADIlgQIRgAA +ABIAAACIAgAA2JYECLoBAAASAAAAcQMAAOiWBAj4AAAAEgAAADMCAAD4lgQIOQAAABIAAACSAAAA +0FkFCAAAAAASAA0AbwEAAAiXBAhGAAAAEgAAAAwDAAAYlwQIgAEAABIAAACNAwAAKJcECHEAAAAS +AAAA4wEAADi XBA i5 AQAAEgAAAJACAABI +l +w +Q +I +V +w +A +A +A +B +I +A +A +A + +A nAQAAWJcECEoAAAA SAAAA2wMAAGiX +BAiGAAAAEgAAAK0CAAB4lwQIOgAAABIAAABrAAAAiJcECAABAAASAAAAaAIAAJiXBAj1CwAAEgAA +AMsCAAColwQIsAEAABIAAAAqAwA AuJcECBABAAASAAAAEwEAAMiXBAhOAAAAEgAAABsCAADYlwQI +PQAAABIAAACYAAAA6JcECBIBAAASAAAA8QIAAPiXBAgkAQAAEgAAAAUEAAAImAQI0QAAABIAAAAO +AgAAGJgECOACAA AS AAA A2gEAACiYBAjTAAAAEgAAAA +oEAABEpgUIAAAAABAA8f+mAAAA0KQFCAAA +AAARAPH/wAAAADiYBAixAAAAEgAAAB0EAADwqQ UIA AAAABAA8f/oAAAASJgE +CD +wA +AAASA +AAA +pw +E +A +AFiYBAhmAQAAEgAAAJkBAABomAQINQAAABIAAADEAgAAeJgECFIAAAASAAAAVQAAAIiYBAhKAQAA +EgAAA AIDAACYmAQIRAAAABIAAAD1AwAAqJgECBkAAAASAAAAeAAAALiYB AhHAAAAEgAAAHgCAADI +mAQIpgAAABIAAACFAwAA2JgECCQAAAASAAAAgAIAAOiYBAiiAAAAEgAAAKABAABopgUIBAAAABEA +FwChAwAABFoFCAQAAAARAA4ADAAAAPiYBAhvAAAAEgAAAPoCAAAImQQINAAAABIAAAAkAwAAGJkE +CKwAAAASAAAAGgAAAAAAAAAAAAAAIAAAADUDAAAomQQIcQAAABIAAABTAgAAbKYFCAQAAAARABcA + + + +5AMAADiZBAgQAQAAEgAAAC4AAAAAAAAAAAAAACAAAADhAAAASJkECDAAAAASAAAAAGxpYnJ0LnNv +LjEAY2xvY2tfZ2V0dGltZQBfSnZfUmVnaXN0ZXJDbGFzc2VzAF9fZ21vbl9zdGFydF9fAGxpYmFj +bC5zby4xAGFjbF9lbnRyaWVzAGFjbF9nZXRfZmlsZQBfRFlOQU1JQwBhY2xfc2V0X2ZpbGUAYWNs +X2RlbGV0ZV9kZWZfZmlsZQBfaW5pdABfZmluaQBhY2xfZnJvbV90ZXh0AF9HTE9CQUxfT0ZGU0VU +X1RBQkxFXwBhY2xf Zn +JlZQBhY2xfZXh0ZW5kZWRfZmlsZQBsaWJjLnNvLjYAc3RyY3B5AGlvY3Rs +AHN0ZG91dAByZWFkZGlyNjQAX19zdHJ0b3VsbF9pbnRlcm5hbABzaWdlbXB0eXNldABtZW1tb3Zl +AGdldG9wdF9sb25nAF9fZnBlbmRpbmcAZ2V0 Z3JnaWQAZ2V0ZW52AGlzd2NudHJsAF9fc3RydG9s +X2ludGVybmFsAHFzb3J0AG1lbWNweQByZWFkbGluawBfX292ZXJmbG93AG1icnRvd2MAbWFsbG9j +AGlzYXR0eQBvcHRhcmcAX29ic3RhY2tfbmV3Y2h1bmsAc2lnYWRkc2V0AGxvY2FsZWNvbnYAYWJv +cnQAY2htb2QAaXN3cHJpbnQAc3RycmNocgBfX2N0eXBlX3RvbG93ZXJfbG9jAF9vYnN0YWNrX2Jl +Z2luAGNhbGxvYwB3cml0ZQBfX2N0eXBlX2dldF9t Yl9jdXJfbWF4AGZwcmludGYAZnB1dHNfdW5s +b2NrZWQAZGNnZ XR0ZXh0AG9wdGluZABmbm1hdGNoAGRpcmZkAHN0cmNvbGwAc3RybmNtcABzdHJu +Y3B5AHdjd2lkdGgAcmVhbGxvYwBfX3N0cmR1cABzaWdhY3Rpb24AX194c3RhdDY0AGdldHRpbWVv +ZmRheQBsb2NhbHRpbWUAbWVtc2V0AG9wZW5kaXIAX19hc3NlcnRfZmFpbABzdHJjbXAAZ2V0cHd1 +aWQAZ2V0cHduYW0Ac3ByaW50ZgBfX21lbXBjcHkAZmNsb3NlAHNldGxvY2FsZQBzdGRlcnIAZXJy +b3IAX19seHN0YXQ2NABfX2N0eXBlX2JfbG9jAHN0cmZ0aW1lAGZ3cml0ZV91bmxvY2tlZABfX2Vy +cm5vX2xvY2F0aW9uAGJpbmR0ZXh0ZG9tYWluAGdldGdybmFtAF9zZXRqbXAAX19jdHlwZV90b3Vw + + +cGVyX2xvYwBfSU9fc3RkaW5fdXNlZABzdHJ2ZXJzY21wAF9fbGliY19zdGFydF9tYWluAHN0cmxl +bgBzdHJjaHIAY2xvc2VkaXIAX19meHN0YXQ2NAByYWlzZQBtYnNpbml0AF9fY3hhX2F0ZXhpdABf +ZWRhdGEAX19ic3Nfc3RhcnQAX2VuZABHTElCQ18yLjIAQUNMXzEuMABHTElCQ18yLjIuMwBHTElC +Q18yLjEuMwBHTElCQ18yLjEAR0xJQkNfMi4zAEdMSUJDXzIuMAAAAAIAAgADAAQAAgADAAIAAgAB +AAUAAgACAAIABgACAAIAAgACAAIABgACAAcAAgACAAIAAwACAAEABAACAAIAAgACAAIAAgAEAAgA +AgACAAIAAgACAAIAAgACAAIABgACAAIAAwABAAIAAgACAAIAAgABAAIABgAFAAIAAgACAAIAAgAE +AAIAAgADAAIAAgAEAAIAAgACAAIAAQABAAIAAQACAAIAAgACAAQAAgACAAQAAgACAAIAAgABAAkA +AgACAAAABQACAAMAAAACAAEAAQABAAAAEAAAACAAAAASaWkNAAAJACIEAAAAAAAAAQABAD0AAAAQ +AAAAIAAAAFAkggUAAAQALAQAAAAAAAABAAYA1wAAABAAAAAAAAAAcxppCQAACAA0BAAAEAAAAHMf +aQkAAAcAQAQAABAAAAARaWkNAAAGAEwEAAAQAAAAE2lpDQAABQBWBAAAEAAAABJpaQ0AAAMAIgQA +ABAAAAAQaWkNAAACAGAEAAAAAAAAQKYFCAZlAABgpgUIBSEAAGSmBQgFIgAAaKYFCAVcAABspgUI +BWMAANykBQgHAQAA4KQFCAcCAADkpAUIBwMAAOikBQgHBAAA7KQFCAcFAADwpAUIBwYAAPSkBQgH +BwAA+KQFCAcIAAD8pAUIBwoAAAClBQgHCwAABKUFCAcMAAAIpQUIBw0AAAylBQgHDgAAEKUFCAcP +AAAUpQUIBxAAABilBQgHEQAAHKUFCAcSAAAgpQUIBxMAACSlBQgHFAAAKKUFCAcVAAAspQUIBxYA +ADClBQgHFwAANKUFCAcYAAA4pQUIBxkAADylBQgHGgAAQKUFCAcbAABEpQUIBx0AAEilBQgHHgAA +TKUFCAcfAABQpQUIByAAAFSlBQgHIwAAWKUFCAckAABcpQUIByUAAGClBQgHJgAAZKUFCAcnAABo +pQUIBygAAGylBQgHKQAAcKUFCAcqAAB0pQUIBysAAHilBQgHLAAAfKUFCActAACApQUIBy4AAISl +BQgHLwAAiKUFCAcwAACMpQUIBzEAAJClBQgHMgAAlKUFCAc0AACYpQUIBzUAAJylBQgHNgAAoKUF +CAc3AACkpQUIBzgAAKilBQgHOgAArKUFCAc7AACwpQUIBzwAALSlBQgHPQAAuKUFCAc+AAC8pQUI +Bz8AAMClBQgHQAAAxKUFCAdBAADIpQUIB0IAAMylBQgHQwAA0KUFCAdEAADUpQUIB0UAANilBQgH +RgAA3KUFCAdHAADgpQUIB0gAAOSlBQgHSQAA6KUFCAdKAADspQUIB0sAAPClBQgHTAAA9KUFCAdP +AAD4pQUIB1EAAPylBQgHUgAAAKYFCAdTAAAEpgUIB1QAAAimBQgHVQAADKYFCAdWAAAQpgUIB1cA +ABSmBQgHWAAAGKYFCAdZAAAcpgUIB1oAACCmBQgHWwAAJKYFCAdeAAAopgUIB18AACymBQgHYAAA +MKYFCAdiAAA0pgUIB2QAADimBQgHZgAAVYnlg+wI6MkFAADoMAYAAOjbxQAAycMA/zXUpAUI/yXY +pAUIAAAAAP8l3KQFCGgAAAAA6eD/////JeCkBQhoCAAAAOnQ/////yXkpAUIaBAAAADpwP////8l +6KQFCGgYAAAA6bD/////JeykBQhoIAAAAOmg/////yXwpAUIaCgAAADpkP////8l9KQFCGgwAAAA +6YD/////JfikBQhoOAAAAOlw/////yX8pAUIaEAAAADpYP////8lAKUFCGhIAAAA6VD/////JQSl +BQhoUAAAAOlA/////yUIpQUIaFgAAADpMP////8lDKUFCGhgAAAA6SD/////JRClBQhoaAAAAOkQ +/////yUUpQUIaHAAAADpAP////8lGKUFCGh4AAAA6fD+////JRylBQhogAAAAOng/v///yUgpQUI +aIgAAADp0P7///8lJKUFCGiQAAAA6cD+////JSilBQhomAAAAOmw/v///yUspQUIaKAAAADpoP7/ +//8lMKUFCGioAAAA6ZD+////JTSlBQhosAAAAOmA/v///yU4pQUIaLgAAADpcP7///8lPKUFCGjA +AAAA6WD+////JUClBQhoyAAAAOlQ/v///yVEpQUIaNAAAADpQP7///8lSKUFCGjYAAAA6TD+//// +JUylBQho4AAAAOkg/v///yVQpQUIaOgAAADpEP7///8lVKUFCGjwAAAA6QD+////JVilBQho+AAA +AOnw/f///yVcpQUIaAABAADp4P3///8lYKUFCGgIAQAA6dD9////JWSlBQhoEAEAAOnA/f///yVo +pQUIaBgBAADpsP3///8lbKUFCGggAQAA6aD9////JXClBQhoKAEAAOmQ/f///yV0pQUIaDABAADp +gP3///8leKUFCGg4AQAA6XD9////JXylBQhoQAEAAOlg/f///yWApQUIaEgBAADpUP3///8lhKUF +CGhQAQAA6UD9////JYilBQhoWAEAAOkw/f///yWMpQUIaGABAADpIP3///8lkKUFCGhoAQAA6RD9 +////JZSlBQhocAEAAOkA/f///yWYpQUIaHgBAADp8Pz///8lnKUFCGiAAQAA6eD8////JaClBQho +iAEAAOnQ/P///yWkpQUIaJABAADpwPz///8lqKUFCGiYAQAA6bD8////JaylBQhooAEAAOmg/P// +/yWwpQUIaKgBAADpkPz///8ltKUFCGiwAQAA6YD8////JbilBQhouAEAAOlw/P///yW8pQUIaMAB +AADpYPz///8lwKUFCGjIAQAA6VD8////JcSlBQho0AEAAOlA/P///yXIpQUIaNgBAADpMPz///8l +zKUFCGjgAQAA6SD8////JdClBQho6AEAAOkQ/P///yXUpQUIaPABAADpAPz///8l2KUFCGj4AQAA +6fD7////JdylBQhoAAIAAOng+////yXgpQUIaAgCAADp0Pv///8l5KUFCGgQAgAA6cD7////Jeil +BQhoGAIAAOmw+////yXspQUIaCACAADpoPv///8l8KUFCGgoAgAA6ZD7////JfSlBQhoMAIAAOmA ++////yX4pQUIaDgCAADpcPv///8l/KUFCGhAAgAA6WD7////JQCmBQhoSAIAAOlQ+////yUEpgUI +aFACAADpQPv///8lCKYFCGhYAgAA6TD7////JQymBQhoYAIAAOkg+////yUQpgUIaGgCAADpEPv/ +//8lFKYFCGhwAgAA6QD7////JRimBQhoeAIAAOnw+v///yUcpgUIaIACAADp4Pr///8lIKYFCGiI +AgAA6dD6////JSSmBQhokAIAAOnA+v///yUopgUIaJgCAADpsPr///8lLKYFCGigAgAA6aD6//// +JTCmBQhoqAIAAOmQ+v///yU0pgUIaLACAADpgPr///8lOKYFCGi4AgAA6XD6//8AAAAAAAAAADHt +Xonhg+TwUFRSaABZBQhooFgFCFFWaLCdBAjoN/3///SQkFWJ5VPoAAAAAFuBw0MLAQBQi4NwAQAA +hcB0Av/Qi138ycOQkJCQkJCQkJCQVYnlg+wIgD1wpgUIAHUtoWiiBQiLEIXSdBuNtgAAAACDwASj +aKIFCP/SoWiiBQiLEIXSdevGBXCmBQgBycOJ9lWJ5YPsCKHMpAUIhcB0IbgAAAAAhcB0GMcEJMyk +BQjo7GX79422AAAAAI2/AAAAAInsXcOQkJCQkJCQkJCQkJBVieWD7BiJXfyLFcynBQiLDcinBQiL +XQiJ0CnIg/gPdlGh0KcFCCnQg/jwfCiJHCSNQvC6EAAAAIlEJASjzKcFCIlUJAjoLvv//4nYi138 +iexdwgQAxwQkwKcFCLnw////iUwkBOi+/f//ixXMpwUI67vHBCRgagUIuC9cBQi7O1wFCIlEJAy4 +awMAAIlEJAiJXCQE6B/6//+NtCYAAAAAVYnlV1ZTg+wci00Mi0UIi1EMi3kIiUXwidYp/sHuAoX2 +fl45+g+ElQAAAItBGItZEAHCiV3s99AhwolRDItBBCnCKcM52n4Gi0XsiUEMi0EMiUEIoWCmBQiJ +RCQEi13wiRwkMdvoXvn//znzfDihYKYFCItQFDtQGHMVxgIK/0AUg8QcW15fXcONtCYAAAAAx0UM +CgAAAIlFCIPEHFteX13p0vj//4sEn0PHBCRAXAUIiUQkBOhu+///OfN86OuugEkoAuli////jbQm +AAAAAFWJ5YPsGItFCItNDItQBIsAiUwkCDHJiUwkDIkEJIlUJAToGbsAAInsXcOQjXQmAFWJ5YPs +EIl9/It9CIl1+It1DIld9ItfBMZF8wCLBotWBIsPid4x1jHICcZ1I4t1DItfDItPCItGCItWDIne +Mcgx1gnGdQrGRfMBjbYAAAAAD7ZF84td9It1+It9/InsXcPrDZCQkJCQkJCQkJCQkJBVieVd6e/7 +//+NtCYAAAAAVYnlg+woiV30i1UMi0UIiXX4i10Qi3UUiX38iVXsiUXoxwQkEAAAAOhjpQAAiRiJ +x4tV7IlwBItF6IlXDIlHCKGEpgUIiXwkBIkEJOhPegAAhcB0JTHbOfgPlcOF23UQidiLdfiLXfSL + +ffyJ7F3DkIk8JOhw+///6+booaQAAJBVieWD7AiJXfyLXQiLA4XAdR+LQwSFwHUOiV0Ii138iexd +6UH7//+JBCToOfv/ +/+voiQQk6C/7///r15CNdCYAVYnlg+ +wIxwQkoKIFCOjuQwAAhcB0ConsXcON +tgAAAADHBCSoogUI6NRDAADr6In2VYnlgeyoAAAAiXX8i3UIiV346Ln///+D/hR0RzHAjZ1o//// +iYVo////jYVs////iQQk6FD6///HRewAAAAAMcCJRCQIiVwkBIk0JOiH9v//iTQk6C/5//+LXfiL +dfyJ7F3DvhMAAADr5422AAAAAFWJ5VdWU4Hs3AEAALtFXAUIi1UMg+TwiwKJXCQExwQkBgAAAKPs +qQUI6Cr3///HBCRGXAUIuVBcBQiJTCQE6PX3///HBCRGXAUI6On4/ +//HBCRwHQUI6FW7AAC4AQAA +ADHSowinBQi4AQAAAKMMpwUIMcCJFRynBQiLVQijmKYFCItFDIkUJIlEJATooAUAAInDodCmBQiF +wA+FlgQAAIM94KYFCAEPhFgEAACh5KYFCIXAD4XiAwAAoaimBQgx0oPoA4P4AXYooaCmBQiFwHQf +gz3gpgUIBXQWobimBQiFwHUNiz3cpgUIhf90CI12ALoBAAAAiRUUpwUIMcCF0nUkizXkpgUIhfZ1 +FYsN0KYFCIXJdQuLFcymBQiF0nQGkLgBAAAAoxinBQihyKYFCIXAD4ULAwAAxwQk4C4AALhkAAAA +o4ymBQjo46IAAKOIpgUIMcCjkKYFCOiSHgAAi3UIKd4PlcAPtsBIIQUIpwUIO10ID4yYAgAAiw0I +pwUIhcl0PYsV6KYFCIXSD4RpAgAAxwQkYlwFCLhFXAUIiUQkDLgBAAAAiUQkCLgEAAAAiUQkBOio +HgAAkI20JgAAAAChkKYFCIXAD4W9AQAAg/4BD4eqAQAAoZimBQiFwHRri0gIhcl1CjHSiRUMpwUI +ifaFwHRWjZ04/v//jbYAAAAAicaLQAijmKYFCKGEpgUIhcB0CosGhcAPhAMBAACLRgSJRCQEiwaJ +BCToshY AAI k 0 J L 4 B A A A A 6 O X 8 / / + J N Q y nBQihmKYFCIXAdb +aLHcimBQiF23V5odCmBQiFwHVWoYSm +BQiFwHQZiQQk6CJsAACFwHUaoYSmBQiJBCToUXMAAKEcpwUIiQQk6Mz3///HBCQAawUIuGRcBQiJ +RCQMuMMEAACJRCQIuDtcBQiJRCQE6IX0///HBCSgogUI6EFAAADHBCSoogUI6DVAAADrkMcEJGlc +BQi5QKcFCIlMJAToPvr//8cEJHNcBQi6gKcFCIlUJAToKfr//6H8pgUIiQQk6JyTAADHBCRAawUI +iwSFoIsFCIlEJAToLfb//+k0////iRwk6Fj5//+hhKYFCIPsBIlcJASJBCToRHcAAIXAdBWJBCTo +SPv//4k0JOjQ+///6ez+///HBCSAXAUIuGRcBQi/O1wFCIlEJAy4pAQAAIlEJAiJfCQE6TL///+N +dCYAoZimBQjpZv7//+hxLAAAoeimBQiFwHROoZCmBQiFwA+EKP7//+iWLgAAizWYpgUIhfYPhJL+ +//+hYKYFCItQFDtQGHMOxgIK/0AU/wUopwUI662JBCS7CgAAAIlcJATotPL//+vlxwQkRVwFCDH/ +iXwkBOiYIwAA657HBCRiXAUIMcCJRCQE6HQUAADpr/3//7hFXAUIi1UMMf+JRCQMuAEAAACJRCQI +iXwkBIsEmkOJBCToKBwAADtdCHzU6Tf9///HBCRApwUIuDiYBAgx/4lEJBAx9riIlQQIiUQkDIl8 +JAiJdCQE6Ezy///HBCSApwUIMcC5OJgECIlEJAi6iJUECDHAiUwkEIlUJAyJRCQE6CLy///pnPz/ + + + + + + + + + + + + + + +/8cEJB4AAAAx0rmgmwQIiUwkCL9AnAQIvtCbBAiJfCQQiXQkDIlUJAToOG8AAKOEpgUIhcAPhFYB +AADHBCTApwUIuDiYBAiJRCQQuIiVBAiJRCQMMcCJRCQIMcCJRCQE6Lnx///ptfv//6HopgUIhcB1 +EoM9zKYFCAF0CaGgpgUIhcB1D7gCAAAAo+CmBQjpfvv//7gEAAAA6+/oVhAAAKHQpgUIhcAPhFj7 +///oBDoAAKEEowUIhcB1FIs9/KIFCIX/dBWLNaCmBQiF9nULuQEAAACJDdimBQiNhUj+//8x9okE +JOiU9P//jbYAAAAAjb8AAAAAiwS1pGIFCI2VSP7//0aJFCSJRCQE6L7y//+D/gZy4fy6QJ0ECI29 +zP7//4mVyP7//7kgAAAAjbVI/v//McDzpYmFTP///zH2kI20JgAAAACLPLWkYgUIjYVY////iUQk +CDHAiUQkBIk8JOhp8P//g71Y////AXQYiTwkMcCNlcj+//+JRCQIiVQkBOhI8P//RoP+BnK66XT6 +///okJ0AAFUxyYnlV1ZTg+xMoSijBQjHRdAAAAAAi3UIi30Mx0XMAAAAAIP4AokN+KYFCA+ENw0A +AIP4Ag+PBA0AAEh0BeiU8f//xwQkAQAAAOg49P//hcAPhNcMAAC4AgAAAKOgpgUIuAEAAACj+KYF +CMcEJIZcBQgxwDHbo7SmBQgx0rkBAAAAiR2kpgUIMcAx26O4pgUIMcCjzKYFCDHAo9ymBQi4AQAA +AKPgpgUIMcCJDaimBQgxyYkVrKYFCDHSo+SmBQgxwIkd6KYFCIkN7KYFCIkV8KYFCKP0pgUI6Anw +//+FwInDdEKJHCS4BAAAAIlEJAy4wIsFCIlEJAi4oIsFCIlEJAToWHQAAIXAD4jYCwAAxwQkAAAA +AIsEhcCLBQiJRCQE6GmPAADHBCSUXAUI6LXv//+JBCSJwzHSiVQkBLnApgUIiUwkCOikhgAAo7ym +BQiF2w+EdgsAAKHApgUIixXEpgUIo5CiBQiJFZSiBQjHBCSiXAUIuFAAAACjEKcFCOhj7///hcCJ +w3QJgDgAD4W+CgAAxwQkAQAAAI1F4LoTVAAAiUQkCIlUJATouPL//0B0EQ+3ReJmhcB0CA+3wKMQ +pwUIxwQkqlwFCLgIAAAAowSnBQjoDu///4XAD4TjCQAAjbQmAAAAAI28JwAAAACJfCQEMcCJRCQQ +uCBfBQiJRCQMuIBrBQiJRCQIiTQk6GTx//+D+P8PhL8GAAAFgwAAAD0PAQAAD4czBAAA/ySFxGIF +CLoBAAAAuAEAAACJFeymBQij8KYFCOuluLpcBQi6wGsFCIlEJAy4RlwFCIlEJAihKKMFCIlUJBC6 +TVwFCIP4AXQPg/gCusJcBQh0BbrBXAUIiVQkBKFgpgUIiQQk6NuZAADHBCQAAAAA6Ifx///HBCQA +AAAAkI20JgAAAADo20MAAOk2////oaCmBQiFwA+EKf///7gBAAAAo6CmBQjpGv///zHAo/CmBQi4 +AQAAAKPspgUI6QT////HBCTHXAUI6BgWAADHBCTGXAUI6AwWAADp5/7//7gCAAAA67y4AQAAAKPI +pgUI6dH+//+4AQAAAKPMpgUI6cL+//8x24kdjKIFCOm1/v//uQMAAACJDeCmBQjppf7//6FopgUI +iQQk66q4BQAAAKPgpgUI6Yz+//8xwIlEJATHBCQAAAAA6BqNAADpdf7//7gDAAAA6+S4AQAAAKPk +pgUI6V/+//+4BAAAAKOopgUIx0XMAQAAAOlJ/v//McAxyTHbiUQkEI1F1IlEJAyhaKYFCIlcJAiJ +TCQEiQQk6AOcAACFwHURi0XUhcB4CqMEpwUI6Q7+//+haKYFCIkEJOiRlQAAxwQkAAAAAInDugUA +AACJVCQIuMpcBQiJRCQE6Ons//+JRCQIMcCJRCQEiVwkDMcEJAEAAADoD/H//4tF1OuqMcDpYf// +/7gCAAAA6Vf///+4BAAAAOkd////uAEAAACjpKYFCOmY/f//uAEAAACj6KYFCOmJ/f//x0XMAQAA +AIsNoKYFCLgBAAAAo+ymBQgx27gBAAAAo/CmBQiFyYkdqKYFCHQUMdIxwIkVuKYFCKPQpgUI6Uj9 +///HBCQBAAAA6NTv//+FwA+VwA+2wI1QAYkVoKYFCOvNMcCjoKYFCDHAo4iiBQjpFv3//7hwAAAA +o7ymBQi4AQAAALsBAAAAo8CmBQgxwKPEpgUIiR2QogUIMcmJDZSiBQjp4/z//7gBAAAAo9ymBQjp +1Pz//zHAuwAEAAAxyaO8pgUIugAEAACJHcCmBQiJDcSmBQiJFZCiBQjrvTHA6YL9//+4BAAAAOl4 +/f//uAEAAACjtKYFCOvjMcCjoKYFCOnC/f//uAIAAADprv3//7gBAAAAo/imBQjpa/z//7sBAAAA +iR2spgUI6Vv8//+5AQAAAIkNuKYFCOlL/P//ugMAAACJFaimBQjp5v3//7gCAAAA6Y/+//+4BQAA +AOnN/f//McCJRCQQjUXYiUQkDDHAiUQkCDHAiUQkBKFopgUIiQQk6OGZAACFwHURi0XYhcB+CqMQ +pwUI6ez7//+haKYFCIkEJOhvkwAAxwQkAAAAAInDud9cBQiJTCQEuAUAAACJRCQI6Mfq//+JRCQI +MdKJXCQMiVQkBMcEJAEAAADo7e7//4tF2OuquAMAAADpcvz//422AAAAAMcEJAEAAADpRPz//8YF +sKYFCAHpePv//7jApgUIiUQkCLgBAAAAiUQkBKFopgUIiQQk6FmBAACjvKYFCIsVxKYFCKHApgUI +iRWUogUIo5CiBQjpOfv//4sVaKYFCLgBAAAAhdJ1ODHbg/gBdCqD+AJ0FYkd0KYFCIXbD4QQ+/// +McDp8fz//8cEJAEAAADole3//4XAdNu7AQAAAOvUiVQkBKEsowUIuQQAAACJTCQQxwQk9lwFCIlE +JBS4gGIFCIlEJAy4QGIFCIlEJAjo3nAAAIsEhYBiBQjri7oEAAAAiRXgpgUI6aX6///HBCT+XAUI +oSyjBQiJRCQUuAQAAACJRCQQuMBhBQiJRCQMuKBhBQiJRCQIoWimBQiJRCQE6IxwAACLFIXAYQUI +iRWgpgUI6Vr6///HRdAIWgUIMduJHaCmBQjpRvr//8cEJAddBQihLKMFCIlEJBS4BAAAAIlEJBC4 +uF4FCIlEJAy4qF4FCIlEJAihaKYFCIlEJAToLXAAAIsEhbheBQjpNfv//8cEJBldBQihLKMFCLnA +iwUIiUwkDLqgiwUIuwQAAACJXCQQiUQkFKFopgUIiVQkCIlEJATo6G8AAIsEhcCLBQjpMvv//zHA +6UD9//+4UAAAAOma/P//xwQkKV0FCKEsowUIufRhBQiJTCQMutxhBQi7BAAAAIlcJBCJRCQUoWim +BQiJVCQIiUQkBOiSbwAAiwSF9GEFCOkM+///xwQkMF0FCKEsowUIiUQkFLgEAAAAiUQkELggYgUI +iUQkDLgIYgUIiUQkCKFopgUIiUQkBOhNbwAAiwSFIGIFCOl/+///oWimBQiJRdDpFPn//8cEJAAA +AADoCIcAAKP8pgUIiQQk6HuHAACD+AQPhKMCAAChzKYFCIXAdD6NmDZdBQgPtoA2XQUIhMB0LZCN +tCYAAAAAD77AQ74BAAAAiUQkBKH8pgUIiXQkCIkEJOhyhwAAD7YDhMB128cEJAAAAADon4YAAKMA +pwUIuQEAAAC6OgAAAIlMJAiJVCQEiQQk6ECHAAChyKYFCIXAD4QZAgAAixWgpgUIhdJ0BzHAo8im +BQihpKYFCEiD+AF3FYtFzIXAdQ6F0nQOuAMAAACjqKYFCIXSdUGLXdCF2w+EuQEAAI12AIkcJL8G +AAAAvrxiBQiJfCQIiXQkBOje6P//hcB1IscEJAIAAADoZnIAAIXAdAWDwwbrzaFspgUIg8RMW15f +XcOAOysPhNsAAACJXCQEoSyjBQi5BAAAAIlMJBC6lF4FCIlUJAyJRCQUuIBeBQiJRCQIxwQkPF0F +COjTbQAAiwSFlF4FCIP4AQ+EhgAAAIP4AXJog/gCdEmD+AN1l8cEJAIAAADo6HEAAIXAdIcx28cE +JAAAAACLBJ0YowUIvgIAAACJdCQIiUQkBOhq5v//iQSdGKMFCEOD+wF20+lT////uEddBQi/bF0F +CKMYowUIiT0cowUI6Tn///+4UV0FCKMcowUIuFFdBQijGKMFCOkg////uGldBQijHKMFCLhpXQUI +6+W5CgAAAI17AYlMJASJPCToYOX//4XAicZ0dI1AAboKAAAAiUXIiVQkBIkEJOhD5f//hcB0Qok8 +JOivhAAAxwQkAAAAAInDuAUAAACJRCQIuHhdBQiJRCQE6Lfl//+JXCQMMduJRCQIiVwkBMcEJAEA +AADo3en//8YGAIt1yIk9GKMFCIk1HKMFCOmB/v//if7r68cEJJVdBQjoZuX//4XAicMPhTT+//+7 +oF0FCOkq/v//ixWgpgUI6e39//+4AQAAAL8gAAAAiUQkCKH8pgUIiXwkBIkEJOjwhAAA6Tn9///H +BCSvXQUI6Bfl//+FwInDD4QV9v//iRwkMcCJRCQQjUXciUQkDDHAiUQkCDHAiUQkBOjUkwAAhcB1 +C4tF3IXAD4nN9///iRwk6G2NAADHBCQAAAAAicO4BQAAAIlEJAi4AGwFCIlEJAToxeT//4lEJAgx +yYlcJAyJTCQExwQkAAAAAOjr6P//6Z71//+JHCQxwIlEJBCNReyJRCQMMcCJRCQIMcCJRCQE6F2T +AACFwHURi0XshcB+CqMQpwUI6Qz1//+JHCTo8IwAAMcEJAAAAACJw7gFAAAAiUQkCLhAbAUIiUQk +BOhI5P//iUQkCDHJiVwkDIlMJATHBCQAAAAA6G7o///pxfT//8cEJJdcBQjoDeT//4XAD4SM9P// +6XH0//+JHCTokIwAAMcEJAAAAACJw7gFAAAAiUQkCLiAbAUIiUQkBOjo4///iVwkDDHbiUQkCIlc +JATHBCQAAAAA6A7o///p+PP//7gBAAAAo6CmBQgxwOkn8///g/gDD4X28v//McCjoKYFCMcEJAAA +AAC4BAAAAIlEJAToK4MAAOkC8///ugIAAACJFaCmBQjr2Yn2jbwnAAAAAFUx0onlV1ZTg+wEi0UM +x0XwAAAAAIsYi0UIizCNdCYAMf+NtCYAAAAAjbwnAAAAAP8kvQRnBQgPtgsPvsGD+D10Z4P4PX9J +hcB0OYP4OnQ0iA5DRv9F8In2jbwnAAAAAIP/BHbLi0UIg/8GiTCLRQyJGItF8HQGW1teX13DuP// +///r878FAAAA69qQjXQmAIP4XHQNg/hedba/BAAAAEPrjr8BAAAA6/aLRRCFwHSg69IPtgMPvsiD ++Xh3c/8kjRhnBQgPvsC/AgAAAI1Q0JBD642/BgAAAOv2un8AAACQg/8BdeuIFjH/Rv9F8OvhuiAA +AADr6roHAAAA6+O6CAAAAOvcuhsAAADr1boMAAAA6866CgAAAOvHug0AAADrwLoJAAAA67m6CwAA +AOuyifYPvtDrq78DAAAAMdLrkon2D7YLiMgsMDwHdwwPvsGNVNDQ6Xn///+IFkb/RfDpvv7//w+2 +Cw++wYPoMIP4Nnfn/ySF/GgFCMHiBA++wY1UENDpSv///8HiBA++wY1UEMnpO////8HiBA++wY1U +EKnpLP///w+2AzH/PD9+Ezx/dA8kH0OIBv9F8Ebpcf7//5A8P3QKvwYAAADpkv7//8YGf+vjjbYA +AAAAjbwnAAAAAFWJ5VdWU4PsPMcEJLddBQjoc+H//4lF1IXAdAWAOAB1CIPEPFteX13Dx0XMAAAA +AL4BAAAAZsdF2D8/xkXaAIkEJOhZjwAAo9SmBQiJRdCQg/4CD4R4AgAAg/4CD49BAQAAg/4BD4Sa +AAAAhfZ/4YX2eCqDPdCiBQgGdaX8izXUogUIv8FdBQi5BgAAAPOmdZC4AQAAAKOUpgUI64THBCQA +AAAAuAUAAACJRCQIuOBsBQiJRCQE6N3g//+JRCQIMcCJRCQExwQkAAAAAOgH5f//odSmBQiJBCTo +GuT//4sdgKYFCIXbdBGJ2ItbEIkEJOgD5P//hdt17zHAo9CmBQjpbv///4tV1A++AoP4KnQ4g/gq +fyOFwA+EVv///w+2Ar4CAAAAiEXYjUIBkI10JgCJRdTpGP///4P4OnXgjUIBiUXU6SP////HBCQU +AAAAvgQAAADoR40AAIlFzItVzKGApgUI/0XUiUIQi0XQiRWApgUIiUIEuAEAAACJRCQIjUXUiUQk +BI1F0IkEJOiP/P//i1XMiQKFwA+Jsv7//+n6/v//g/4DdE+D/gQPhbr+//+LVdSNQgGJRdSAOj10 +Cr7/////6aL+//+LRdC+AQAAAItVzIlCDI1F1DHSiUQkBI1F0IlUJAiJBCToMfz//4tVzIlCCOuf +i1XUvv////+NQgGJRdSAOj0PhY/+//+h4F4FCDHbhcB0LI192I10JgCJRCQEiTwk6Dzf//+FwHRf +Q4sEneBeBQiFwHXkg/7/D4Um/v//jUXYv8hdBQiJBCToy4cAAIl8JASJw7gFAAAAiUQkCMcEJAAA +AADoKN///4lEJAgxyYlcJAyJTCQExwQkAAAAAOhO4///6dz9//+LVdC4oKIFCL4BAAAAiVTYBDHA +iUQkCI1F1IlEJASNRdCJBCToaPv//4kE3aCiBQiFwA+Jif3//77/////6XT///+LRdQPthCE0g+E +vv3//4hV2UC+AwAAAOlD/v//jXYAVYnlg+wYiXX4i3UMiV30iX38i30IxwQkDAAAAOiPiwAAicMx +wIX2dAiJNCTof4wAAIlDBDHAhf90CIk8JOhujAAAiQOhmKYFCIlDCIt1+IkdmKYFCIt9/Itd9Ins +XcOQVTHAieVXMf9WU4HsLAMAAIm99Pz//4mF8Pz//+iH3v//xwAAAAAAicOLRQiJBCTo5OD//4XA +iccPhAMGAACLHYSmBQiF2w+EkAAAAIkEJOhE3///hcCJwg+IxAUAAIlUJASNRYiJRCQIxwQkAwAA +AOgz4v//hcAPiEoFAACLReCLVeSJRCQIi0WIiVQkDItVjIkEJIlUJAToI+X//4XAD4XYBAAAodCn +BQiLFcynBQgp0IP4EA+MogQAAI1CEInRi1WMo8ynBQiLRYiJUQyJQQiLReCLVeSJAYlRBOgvBgAA +6MLd//+JxpCNtCYAAAAAxwYAAAAAiTwk6Jrc//+FwInDdHGJBCTohAUAAIXAdOAPtkMSMdI8BnRW +PAJ0UjwEdE48AXRKPAp0RjwIdEI8DHQ+jbQmAAAAAI28JwAAAACJVCQEi0UIiUQkDDHAiUQkCI1D +E4kEJOgkBgAAAYXw/P//EZX0/P//64aNtgAAAAAPttDry4sehdsPhc8DAACF/3QMiTwk6E3f//+F +wHRWi1UIiRQk6DaHAADHBCQAAAAAuQUAAAC64F0FCIlMJAiJxolUJATojtz//4nD6Ofc//+JdCQM +iVwkCIsAxwQkAAAAAIlEJAToreD//7gBAAAAoxynBQjoNhUAAKHkpgUIhcAPhTEDAAChDKcFCIXA +D4TaAAAAoSCjBQiFwHUdoWCmBQiLUBQ7UBgPg/QCAADGAgr/QBT/BSinBQiLHcimBQgx9ok1IKMF +CIXbD4VDAgAAMcCJRCQMoQCnBQiJRCQIi0UMhcAPhCECAACJRCQEoWCmBQiJBCTo9R8AAAEFKKcF +CKHIpgUIhcB0NosVjKcFCI1CBDsFkKcFCA+HzAEAAIkUJL4EAAAAuyinBQiJdCQIiVwkBOjP3f// +gwWMpwUIBMcEJPVdBQihYKYFCLkCAAAAiUwkCLoBAAAAiVQkBIlEJAzokdv//4MFKKcFCAKhoKYF +CIXAdA2huKYFCIXAD4TwAAAAocimBQiFwA+FJwEAAMcEJAAAAAC4BQAAAL/4XQUIiUQkCIl8JATo +Kdv//4nDoWCmBQiJHCSJRCQE6Nba//+JHCToPtz//wEFKKcFCKFgpgUIi1AUO1AYD4PBAAAAxgIg +/0AU/wUopwUIMcmhwKYFCIlMJBSLFcSmBQi7AAIAAIlcJBCJRCQYobymBQiJVCQci5X0/P//iUQk +DI2F+Pz//4lEJAiLhfD8//+JVCQEiQQk6KZnAACJw6FgpgUIiRwkiUQkBOhL2v//iRwk6LPb//8B +BSinBQihYKYFCItQFDtQGHMnxgIK/0AU/wUopwUIoZCmBQiFwHULgcQsAwAAW15fXcPoYxUAAOvu +iQQkugoAAACJVCQE6KjZ///rzIkEJL4gAAAAiXQkBOiV2f//6S/////HBCT+XQUIoWCmBQiJRCQM +uAIAAACJRCQIuAEAAACJRCQE6Bna//+DBSinBQgC6ab+///HBCSApwUIvwQAAACJfCQE6Fjd//+L +FYynBQjpFP7//4tFCOnX/f//xwQk/l0FCKFgpgUIuQIAAACJTCQIugEAAACJVCQEiUQkDOi+2f// +gwUopwUIAqHIpgUIhcAPhIL9//+LFYynBQiNQgQ7BZCnBQh3JokUJLgEAAAAiUQkCLgopwUIiUQk +BOiP2///gwWMpwUIBOlL/f//xwQkgKcFCLgEAAAAiUQkBOi+3P//ixWMpwUI672JBCS/CgAAAIl8 +JAToldj//+n8/P//uAEAAACJRCQEi0UIiQQk6HQJAACh5KYFCIXAD4W7/P//6an8//+JPCTogtv/ +/4ke6TL8///HBCTApwUIuBAAAACJRCQE6Fbc//+LFcynBQjpPvv//4tFCIkEJOhIgwAAxwQkAAAA +AInDuAUAAACJRCQIuCBtBQiJRCQE6KDY//+JRCQIMcCJXCQMiUQkBMcEJAAAAADoxtz//+km/v// +i1UIiRQk6P6CAADHBCQAAAAAuQUAAAC6YG0FCIlMJAiJxolUJAToVtj//4nD6K/Y//+JdCQMiVwk +CIsAiUQkBMcEJAAAAADoddz//7gBAAAAoxynBQjpy/3//8cEJAMAAACNRYiJRCQIi0UIiUQkBOjc +2f//6TT6//+LVQi+TocFCIkUJOh/ggAAiUQkDIl0JAiLA+umjXYAVYnlg+wIxwQkCAAAAOjehAAA +i1UIiRCLFfSmBQiJUASj9KYFCInsXcONtCYAAAAAVYnlV1ZTg+wMix30pgUIi30Ihdt0Lo13E420 +JgAAAACJdCQEuAQAAACJRCQIiwOJBCTogdj//zHShcB0G4tbBIXbddyh8KYFCIXAdQaAfxMudA+6 +AQAAAIPEDInQW15fXcOh7KYFCIXAdA2AfxQAdAdmg38ULnXbMdLr3I10JgBVieVWMfZTg+wQOzWQ +pgUIfTcx2422AAAAAI2/AAAAAKGIpgUIiwQYiQQk6Gja//+hiKYFCItEGGSFwHUkRoPDeDs1kKYF +CHzXMcCjkKYFCLgEAAAAo5ymBQiDxBBbXl3DiQQk6C/a///r0pCNdCYAVYnlV1ZTgewsAwAAiw2Q +pgUIOw2MpgUIi10MD4RFBQAAiciLNYimBQgx0sHgBCnIweADiVQGZDHJifeJTAZoMdKJVAZsi0UQ +hcAPhbAAAAChFKcFCIXAD4WjAAAAoRinBQiFwHQahdsPhJIAAACD+woPhIkAAACD+wh0a410JgCL +FZCmBQgxyYmN8Pz//4nQweAEKdDB4AMx0olcB3DB4wyJXAcUiZX0/P//kI10JgChkKYFCItVCInD +iRQkweMEKcPB4wPoBoQAAIkEHouV9Pz//4uF8Pz///8FkKYFCI1l9FteX13Dgz3MpgUIAXQQodCm +BQiFwHSHjbQmAAAAAItFCIA4L3QMi1UUgDoAD4UMBAAAi30IoeCmBQiD+ANyEoP4BA+GbAMAAIP4 +BQ+EOQMAAIl8JASLFZCmBQjHBCQDAAAAidDB4AQp0I1ExgSJRCQI6FDY//+Jw4XbD4jIAgAAiw2g +pgUIhckPhEICAACLDZCmBQiLNYimBQiJyMHgBCnIjRTFAAAAAItEFhQlAPAAAD0AoAAAD4QSAQAA +icjB4AQpyMHgA4tUBhSB4gDwAACB+gCgAAAPhOcAAACB+gBAAAAPhJsAAAC/CAAAAIl8BnCJyDHb +weAEKciLVMY8i0zGQIlcJBShwKYFCL4AAgAAiZXw/P//ixXEpgUIiUQkGKG8pgUIiY30/P//iVQk +HIuV9Pz//4lEJAyNhfj8//+JRCQIi4Xw/P//iVQkBIl0JBCJBCTolWEAAIkEJOi11f//OQWcpgUI +fQ+D+Ad+BbgHAAAAo5ymBQiLNYimBQjpS/7//4tVEIXSdCSLPeimBQiF/3Uaux4AAACQjXQmAIlc +BnDpSv///420JgAAAACJyLoEAAAAweAEKciJVMZw6S7///+7CgAAAOvUjXQmAKGgpgUIhcB0DaHY +pgUIhcAPhNj+//+JPCSNBDKJRCQE6MsCAACLFZCmBQiJ0MHgBCnQixWIpgUIi0TCZIk8JIlEJATo +OAMAAIXAicMPhJoAAACLNcymBQiF9nUOiw3YpgUIhckPhHYAAACJXCQEjUWIiUQkCMcEJAMAAADo +Z9X//4XAdVuLFZCmBQiLNYimBQiJ0MHgBCnQugEAAACJVMZsi0UQhcB0VaGgpgUIhcB0TItNmInI +JQDwAAA9AEAAAHQeixWQpgUIvwEAAACJ0MHgBCnQweADiUwGaIl8BmyQhdt0CIkcJOiM1v//iw2Q +pgUIizWIpgUI6fH9//+LTZjrwIk8JIsVkKYFCInQweAEKdCLFYimBQiNRMIEiUQkBOgLLQAAiw2Q +pgUIizWIpgUIicrB4gQpyoXAD59E1nSFwA+Jhf3//4k8JOhRfQAAicPoItP//4lcJAy6TocFCIlU +JAiLAMcEJAAAAACJRCQE6OPW///pRv3//4k8JL5OhwUI6Bl9AACJw+jq0v//iVwkDLsBAAAAiXQk +CIsAxwQkAAAAAIlEJAToq9b//4kdHKcFCDHAMdLpc/z//4l8JASLFZCmBQjHBCQDAAAAidDB4AQp +0I1ExgSJRCQI6AfU///pwvz//4tFEIXAD4SS/P//iXwkBIsVkKYFCMcEJAMAAACJ0MHgBCnQjUTG +BIlEJAjo0tP//4M94KYFCAOJww+Ehfz//4XAeDqLDZCmBQiLNYimBQiJyMHgBCnIi0TGFCUA8AAA +PQBAAAAPlcAPtsCFwA+EUvz//4s1iKYFCOkg/P//6BDS//+DOAIPlMDr3YtNCIkMJOjN0v//icOL +RRSJBCTowNL//41EGBGLVQiD4PCLTRQpxI18JCCJVCQIiUwkBIk8JOiVJQAAizWIpgUI6bH7//+N +BAnB4QUpwaOMpgUIjQTNAAAAAIlEJAShiKYFCIkEJOhlfgAAo4imBQiLDZCmBQjphvr//5CNdCYA +VYnlg+wYiV34i10IiXX8iRwk6Jl+AACLVQyFwIlCZHQPi134i3X8iexdw5CNdCYAiRwkuwFeBQjo +c3sAAIlcJASJxrgFAAAAiUQkCMcEJAAAAADo0ND//4nD6CnR//+JdCQMiVwkCIsAxwQkAAAAAIlE +JATo79T//7kBAAAAiQ0cpwUI65uNdgCNvCcAAAAAVYnlg+woiX38i0UIi30MiV30iUXwMcCF/4l1 ++HRhgD8vdG24LwAAAIlEJASLRfCJBCTowdL//4XAicZ0U4tF8Ik8JCnGjV4B6HvR//+NRBgBiQQk +6Ed9AACJXCQIicaLRfCJNCSNHDOJRCQE6BfU//+JfCQEiRwk6IvU//+J8Itd9It1+It9/InsXcON +dCYAiX0Ii130i3X4i338iexd6fx9AACNtgAAAACNvwAAAABVieWD7AiLRQiJBCTo31cAADHJgDgu +dAiJ7InIXcOJ9g+2UAGE0nQYgPoudeuAeAIAdeWNtgAAAACNvCcAAAAAuQEAAADr0Yn2jbwnAAAA +AFWJ5VdWU4PsHItFCIA4AHQOixWEpgUIhdIPhV8BAAChkKYFCInDS3g6idjB4AQp2I00xQAAAACJ +9o28JwAAAACLFYimBQiLRDJwg/gED4R9AAAAg/gedHiD7nhLeeKhkKYFCMdF8AAAAAAx2znDfU/8 +ixWIpgUIx0XsAAAAAMdF6AAAAACNdCYAjbwnAAAAAItN7IN8CnAedBz/RfCJzot96INF6HgB1rke +AAAAAdfzpaGQpgUIg0XseEM5w3zRi0Xwo5CmBQiDxBxbXl9dw5CLRQyFwA+FhQAAAIsEMoA4L3QI +i00IgDkAdTWLRDJkiUQkBIsEMokEJOjy7///ixWIpgUIg3wycB4PhUn///+LBDKJBCTo3tH//+k5 +////kDHAiUQkCIsEMolEJASLRQiJBCToCGwAAInHoYimBQiLRDBkiTwkiUQkBOih7///iTwk6KHR +///rpY20JgAAAACLBDKJBCToRf7//4XAD4Xl/v//ixWIpgUI6V3///+JRCQExwQkAAAAAOhi7/// +6Yz+//+NtgAAAACNvCcAAAAAVYnlg+wY6E3O///HAAAAAACLRQyJRCQEi0UIiQQk6JXQ//+J7F3D +ifaNvCcAAAAAVbj/////ieWD7BiJXfiLXQyJdfyLdQiLS1SLVlQ50XwfuAEAAAB/GItLWLj///// +i1ZYOdF8CQ+fwA+2wI12AIXAdAyLXfiLdfyJ7F3DifaLA4lEJASLBokEJOhw////6+KNtCYAAAAA +jbwnAAAAAFW4/////4nlg+wYiV34i10MiXX8i3UIi0tUi1ZUOdF8H7gBAAAAfxiLS1i4/////4tW +WDnRfAkPn8APtsCNdgCFwHQMi134i3X8iexdw4n2iwOJRCQEiwaJBCToyMz//+vijbQmAAAAAI28 +JwAAAABVieWLVQyLRQiJVQiJRQxd6Qv///+NdCYAjbwnAAAAAFWJ5YtVDItFCIlVCIlFDF3pW/// +/410JgCNvCcAAAAAVbj/////ieWD7BiJXfiLXQyJdfyLdQiLS0yLVkw50XwfuAEAAAB/GItLULj/ +////i1ZQOdF8CQ+fwA+2wI12AIXAdAyLXfiLdfyJ7F3DifaLA4lEJASLBokEJOhQ/v//6+KNtCYA +AAAAjbwnAAAAAFW4/////4nlg+wYiV34i10MiXX8i3UIi0tMi1ZMOdF8H7gBAAAAfxiLS1C4//// +/4tWUDnRfAkPn8APtsCNdgCFwHQMi134i3X8iexdw4n2iwOJRCQEiwaJBCToqMv//+vijbQmAAAA +AI28JwAAAABVieWLVQyLRQiJVQiJRQxd6Qv///+NdCYAjbwnAAAAAFWJ5YtVDItFCIlVCIlFDF3p +W////410JgCNvCcAAAAAVbj/////ieWD7BiJXfiLXQyJdfyLdQiLS0SLVkQ50XwfuAEAAAB/GItL +SLj/////i1ZIOdF8CQ+fwA+2wI12AIXAdAyLXfiLdfyJ7F3DifaLA4lEJASLBokEJOgw/f//6+KN +tCYAAAAAjbwnAAAAAFW4/////4nlg+wYiV34i10MiXX8i3UIi0tEi1ZEOdF8H7gBAAAAfxiLS0i4 +/////4tWSDnRfAkPn8APtsCNdgCFwHQMi134i3X8iexdw4n2iwOJRCQEiwaJBCToiMr//+vijbQm +AAAAAI28JwAAAABVieWLVQyLRQiJVQiJRQxd6Qv///+NdCYAjbwnAAAAAFWJ5YtVDItFCIlVCIlF +DF3pW////410JgCNvCcAAAAAVYnlg+wYiX38i0UIi30MiV30iXX4i3c0i0g0i18wi1AwOc58QX8E +OdNyOznOuAEAAAB/DHwEOdN3BjHAjXQmAIXAdA2LXfSLdfiLffyJ7F3DiweLVQiJRCQEiwKJBCTo +DPz//+veuP/////r0412AFWJ5YPsGIl9/ItFCIt9DIld9Il1+It3NItINItfMItQMDnOfEF/BDnT +cjs5zrgBAAAAfwx8BDnTdwYxwI10JgCFwHQNi130i3X4i338iexdw4sHi1UIiUQkBIsCiQQk6GTJ +///r3rj/////69ONdgBVieWLVQyLRQiJVQiJRQxd6Qv///+NdCYAjbwnAAAAAFWJ5YtVDItFCIlV +CIlFDF3pW////410JgCNvCcAAAAAVYnli0UMiwCJRQyLRQiLAIlFCF3pz8r//420JgAAAABVieWL +VQyLRQiJVQiJRQxd686NtCYAAAAAjbwnAAAAAFWJ5YPsCItFDIsAiUQkBItFCIsAiQQk6PT6//+J +7F3DVYnlg+wIi0UMiwCJRCQEi0UIiwCJBCTonMj//4nsXcNVieWLVQyLRQiJVQiJRQxd666NtCYA +AAAAjbwnAAAAAFWJ5YtVDItFCIlVCIlFDF3rro20JgAAAACNvCcAAAAAVYnlg+wYiX38i30IiV30 +uy4AAACJdfiLdQyJXCQEiweJBCTowMr//7kuAAAAicOJTCQEiwaJBCToq8r//4XAdD+JRCQEhduJ +2HQriQQk6D36//+FwHQNi130i3X4i338iexdw4sGiUQkBIsHiQQk6Bz6///r4bhFXAUI686NdgC4 +RVwFCOu6ifaNvCcAAAAAVbguAAAAieWD7BiJffyLfQiJXfSJdfiLdQyJRCQEiweJBCToMMr//4nD +uC4AAACJRCQEiwaJBCToG8r//4XAdD+JRCQEhduJ2HQriQQk6HXH//+FwHQNi130i3X4i338iexd +w4sGiUQkBIsHiQQk6FTH///r4bhFXAUI686NdgC4RVwFCOu6ifaNvCcAAAAAVYnli1UMi0UIiVUI +iUUMXenL/v//jXQmAI28JwAAAABVieWLVQyLRQiJVQiJRQxd6Tv///+NdCYAjbwnAAAAAFWJ5YPs +KIld9KGopgUIiXX4iX38g/gFd2D/JIXYaQUIoaSmBQiD+AEPhHkBAACD+AEPglMBAACD+AJ1PaGs +pgUIhcAPhDcBAAC4MMoECIn2iUXwxwQkAKgFCOjJyv//hcB0MKGopgUIg/gCD4TyAAAAg/gCd1pI +dAXoisf//6GspgUIhcB0P7gwzAQIjXQmAIlF8ItF8IlEJAy4eAAAAIlEJAihkKYFCIlEJAShiKYF +CIkEJOjvx///i130i3X4i338iexdw7jwywQI68ONdgCD+AN0HIP4BHWfoaymBQiFwHQHuHDLBAjr +prjgygQI65+hpKYFCIP4AXRKg/gBcieD+AIPhXD///+LFaymBQiF0nQKuFDKBAjpc////7jAyQQI +6Wn///+LDaymBQiFyXQKuDDJBAjpVf///7igyAQI6Uv///+LHaymBQiF23QKuBDIBAjpN////7iA +xwQI6S3///+hrKYFCIXAdAq4kM0ECOka////uODMBAjpEP///7hQyQQI6cb+//+hrKYFCIXAdAq4 +EMkECOmz/v//uDDIBAjpqf7//6GspgUIhcB0CrjwxwQI6Zb+//+4EMcECOmM/v//oaymBQiFwHQK +uBDMBAjpef7//7jQywQI6W/+//+hrKYFCIXAdAq4cM0ECOlc/v//uFDMBAjpUv7//4n2iz2spgUI +hf90CrhQywQI6Tz+//+4cMoECOky/v//ifaLNaymBQiF9nQKuLDLBAjpHP7//7iQywQI6RL+//+J +9lWJ5YPsGIld+Il1/KGgpgUIg/gEd0r/JIXwaQUIMds7HZCmBQh9OTH2jbQmAAAAAKGIpgUIAfCJ +BCToIQ0AAKFgpgUIi1AUO1AYcyTGAgr/QBRDg8Z4Ox2QpgUIfNKJ9otd+It1/InsXcONtgAAAACJ +BCS5CgAAAIlMJATo18P//+vPMds7HZCmBQh90zH2kKGIpgUIAfCJBCToQQIAAKFgpgUIi1AUO1AY +cxrGAgr/QBT/BSinBQhDg8Z4Ox2QpgUIfMzrmokEJLoKAAAAiVQkBOiBw///69noohgAAItd+It1 +/InsXek0EQAA6I8YAACLXfiLdfyJ7F3pgRMAAItd+It1/InsXekDFgAAjXYAVYnlgey4AAAAiV30 +oSSjBQiJdfiJffyFwHgNi130i3X4i338iexdwzHAjZ14////vmQAAACJhXT///+NhXT///+JBCTo +P8P//4mFcP///4s9GKMFCI10JgCNvCcAAAAAxgMBMcCJRCQUMcCJRCQQi4Vw////iXwkCIl0JASJ +RCQMiRwk6EcjAACFwHUYgDsAdBONBDaJxo1AD4Pg8CnEjVwkGOu7iUQkBDH2iXQkCIkcJOgpXwAA +oySjBQiFwA+JV////zHbMcCJHSSjBQjpSP///422AAAAAI28JwAAAABVieWNRfiD7BiJRCQExwQk +AAAAAOj/xv//hcB1FItF+KOAogUIi0X8o4SiBQiJ7F3DMcCJRCQEjUXwiQQk6FbF//+FwHUYi0Xw +o4CiBQiLRfRpwOgDAAAF5wMAAOvKxwQkAAAAAOiuw///o4CiBQi4/8maO+uyjXYAjbwnAAAAAFWJ +5YPsGIld+ItdCIl1/IsNtKYFCIt1DIXJdDsxwIXAdCaJRCQIuh5eBQiJVCQEiRwk6HHG//+JXQiL +dfyLXfiJ7F3pYMP//4l0JAi4JF4FCIlEJATr2Ik0JOhRWgAA673rDZCQkJCQkJCQkJCQkJBVuogU +AACJ5VeNhUjr//9WU4HsvBwAAI1d2ImVbOP//4tVCIlcJASJhXDj//+LQhSJBCTo+EsAAItNCIB5 +dAAPhNQFAACwK4hF4qGkpgUIxkXjAIP4AQ+EowUAAIP4AQ+CjwUAAIP4Ag+EbAUAAIn2odymBQiL +tXDj//+FwA+F+QQAAKG4pgUIhcAPhXUEAACLVQi5K14FCItCGIlMJASJNCSJRCQMiVwkCOiJxf// +iTQk6IHC//+LFYiiBQgBxoXSD4UlBAAAoYyiBQiFwHQ2obSmBQiFwA+E/AMAADHAhcAPhN4DAACJ +RCQIuB5eBQiJRCQEiTQk6DzF//+JNCToNML//wHGgD2wpgUIAA+FmQMAAItNCItBFCUA8AAAPQAg +AAAPhE8DAAA9AGAAAA+ERAMAAItFCDH/ixWUogUIi1g0i0gwoZCiBQiJXCQEuzReBQiJRCQYuAEA +AACJRCQQobymBQiJVCQciXwkFIlEJAyNhZjj//+JRCQIiQwk6I1NAACJRCQIiVwkBIk0JOilxP// +iTQk6J3B//8Bxo2FdOP//4kEJOgNwP//iYVk4///hcAPhG8CAACLDYCiBQiLlXTj//850Q+MRQIA +ADnRD4QrAgAAjYFUPQ//Mds50 +H8QOcoPjA0CAAA5yg+E8wEAAIs8nRijBQiQjbQmAAAAAMYGAYuF +ZOP//4l8JAiLlWzj//+JNCSLjWjj//+JRCQMi4Vw4///iUwkFDHJAdCJTCQQKfBIiUQkBOjCHwAA +hcB1S4A+AHRGi4Vs4///i5Vw4///AcCJhWzj//+DwA+D4PApxIuFcOP//41cJCCJVCQEiRwkKcaJ +dCQIjTQe6LbB//+JnXDj///pc////wHGxgYgRsYGAKHIpgUIhcAPhQ4BAAChYKYFCLtApwUIiUQk +BIuFcOP//4kEJOgJv///iVwkDItVCIu9cOP//4sNlKYFCItCbCn+ATUopwUIhcmJRCQID4S9AAAA +hcAPhLUAAACLQmiNtgAAAACNvwAAAACJRCQEi1UIiwKJBCTo/wUAAItNCIN5cAp0IosdzKYFCIXb +dQiNZfRbXl9dw4tVCItCFIkEJOh2CAAA6+iLQWSFwHThxwQkOV4FCKFgpgUIMf+JRCQMuAQAAACJ +RCQIuAEAAACJRCQE6Ly+//+JfCQMi1UIgwUopwUIBItCbEiJRCQIi0JoiUQkBItCZIkEJOh8BQAA +izXMpgUIhfZ0hotNCItBaOuMi00Ii0EU6U/////HBCT+XQUIoWCmBQiJRCQMuAIAAACJRCQIuAEA +AACJRCQE6FC+//+DBSinBQgC6b/+//+LlWjj//87FYSiBQgPj/v9//+7AQAAAOnx/f//i4Vo4/// +OQWEogUID43D/f//6Pn6//+LDYCiBQiLlXTj///prf3//42FeOP//4lEJAiLhXTj//+JwYkEJMH5 +H4lMJATo11gAAInD6OD5//+JRCQIuD5eBQiJNCSJXCQMiUQkBOjPwf//iTQk6Me+//8Bxuke/v// +i1UID7ZCJIlEJAyLQiSLUiiJNCQPrNAIJf8AAACJRCQIuENeBQiJRCQE6JHB///p5/z//4tVCItC +HIk0JIlEJATo0vr//wHG6U78//+LTQiLQSCJRCQIuCReBQjpF/z//4tVCItCIIkEJOjJVgAA6fP7 +//+LTQiLQRyJNCSJRCQE6JL6//8BxunC+///ocCmBQi/Pl4FCIsVxKYFCItNCIlEJBi4AAIAAIlE +JBAxwIlEJBShvKYFCIlUJByJRCQMjYUo5v//iU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +QkCItBPItRQIkEJIlUJATovEkAAIlEJAyhnKYF +CIk0JIl8JASJRCQI6MvA//+JNCTow73//wHG6RT7//+LTQiNhbjo//+JRCQIi1Fgi0FciVQkBIkE +JOh1ZwAAiUQkDLgHAAAAiUQkCLg+XgUIiUQkBIuFcOP//4kEJOh5wP//i5Vw4///iRQk6Gu9//+L +tXDj//8Bxump+v//i00Ii1FEi0FIiZV04///iYVo4///6Xz6//+LTQiLUUyLQVDr5ItFCItQVImV +dOP//4tQWImVaOP//+lX+v//sCDpJ/r//1W4/////4nlV1ZTgexMIAAAuwAgAACJXCQEi30MjZ3o +3///iUQkDIt1EIkcJIl8JAiJdCQQ6EZiAACJhdjf//89/x8AAImd1N///w+HZgIAAIs9+KYFCIX/ +D4XRAAAAi0UUhcB0Kuh9vv//g/gBD4eTAAAAi7XU3///i4XY3///ifcBxzHAOf6JhdDf//9yT4t1 +CIX2dCiLRQi7AQAAAIuV2N///4lcJASJRCQMi4XU3///iVQkCIkEJOhJu///i00Uhcl0C4uF0N// +/4tVFIkCi4XY3///jWX0W15fXcPoVL///4sQjXYAjbwnAAAAAA+2BvZEQgFAdAb/hdDf//9GOf5y +6+uJMcCLldTf//+JRCQIi4XY3///iRQkiUQkBOjLVgAAiYXQ3///6WD////os73//4P4AQ+GNwEA +AIu91N///zHbi7XY3///iZ3Q3///ifo +B8omVzN///znXif5zOA+2Fw++woP4Xw+P+QAAAIP4QX0S +g/ggfDKD+CN+CIPoJYP4GncliBZH/4XQ3///Rju9zN///3LIi4XU3///KcaJtdjf///p4/7//zHJ +MdKJjeDf//+JleTf//+NdgCJfCQEjYXg3///jZXc3///iUQkDIuFzN///4kUJCn4iUQkCOiQu/// +g/j/icN0eIP4/nRlhcB1BbsBAAAAi4Xc3///iQQk6O29//+FwInCeDqF23QVjXQmAI28JwAAAAAP +tgdHiAZGS3X2AZXQ3///jYXg3///iQQk6Hq9//+FwA+Eev///+lH////xgY/Ad9G/4XQ3///69eL +vczf///GBj/pJP///0fr9YPoYYP4HekR////i7XU3///i4XY3///ifcBxzn+chGLldjf//+JldDf +///p9v3//+iZvf//icEPthaLAfZEUAFAdQPGBj9GOf5y7OvSicK5/////4PAEIPg8EIpxIl0JBCN +RCQUiYXU3///iUwkDIl8JAiJVCQEiQQk6JdfAADpY/3//4n2VYnlVlOD7BCLDdCmBQiLdQiLVQyF +yYtFEItdFA+F3gAAAIXbdDahyKYFCIXAdC2LUwyNQgQ7QxAPh6gAAACJFCS4BAAAALkopwUIiUQk +CIlMJATo0Lr//4NDDASJdCQEofymBQgx0olUJAyJRCQIoWCmBQiJBCTolPz//wEFKKcFCIXbdDKh +yKYFCIXAdCmLUwyNQgQ7QxB3NokUJLgEAAAAiUQkCLgopwUIiUQkBOh0uv//g0MMBKHQpgUIhcB1 +B4PEEFteXcODxBBbXl3rUIkcJLgEAAAAiUQkBOiXu///i1MM67SJHCS4BAAAAIlEJATogbv//4tT +DOk/////kIlEJAiJVCQEiTQk6EACAADpDf///410JgCNvCcAAAAAVYnlg+wIobSiBQiFwHQRxwQk +sKIFCOiVAwAAiexdw5DHBCSgogUI6IQDAADHBCS4ogUI6HgDAADHBCSoogUI69XrDZCQkJCQkJCQ +kJCQkJBVieWB7LgCAACJXfyh3KYFCItdCIXAD4XUAAAAobimBQiFwHVgMcmLFZSmBQiJTCQMi0Ns +hdKJRCQIdESFwHRAi0NojbYAAAAAjbwnAAAAAIlEJASLA4kEJOgy/v//ocymBQiFwHUJi138iexd +w4n2i0MUiQQk6LUAAADr6o12AItDFOvLocCmBQiLFcSmBQiJRCQYuAACAACJRCQQMcCJRCQUobym +BQiJVCQciUQkDI2FaP3//4lEJAiLQzyLU0CJBCSJVCQE6MRDAACJRCQIoZymBQjHBCQ+XgUIiUQk +BOjDuP//6Tv///+NtgAAAACNhWj9//+JRCQIi0Nci1NgiQQkiVQkBOiEYQAAiUQkCLgHAAAAiUQk +BMcEJD5eBQjog7j//+ny/v//jbYAAAAAVYnlg+wIi00IicqB4gDwAACB+gCAAAB0eIH6AEAAALgv +AAAAdDqB+gCgAAC4QAAAAHQtgfoAEAAAuHwAAAB0IIH6AMAAAHQMkI20JgAAAACJ7F3DuD0AAACN +tCYAAAAAixVgpgUIi0oUO0oYcxKIAf9CFP8FKKcFCOvVkI10JgCJFCQPtsCJRCQE6Dm1///r4oM9 +zKYFCAF1tvbBSbgqAAAAdbzrqo12AI28JwAAAABVieVXvwQAAABWU4PsHItVEItFCItNDIP6/4lF +8A+EPgEAAInIJQDwAAA9AEAAAA+EIgEAAD0AoAAAD4T2AAAAPQAQAAAPhOEAAAA9AMAAAA+EzAAA +AD0AYAAAD4S3AAAAPQAgAAAPhKIAAAD2wUl0D78NAAAAjXYAjbwnAAAAADHbg/8EdDnHBCSgogUI +6O0AAACF240E/aCiBQh0A41DCIkEJOjXAAAAx0UIqKIFCIPEHFteX13pxAAAAI10JgCLRfCJBCTo +Dbb//wFF8InGix2ApgUIhdt0rY22AAAAAIsTOfJ3HItF8IlUJAgp0ItTBIkEJIlUJAToarb//4XA +dIWLWxCF23XX6Xn///+/CgAAAOlo////vwkAAADpXv///78IAAAA6VT///+/BwAAAOlK////hdJ1 +E6EEowUIhcB0Cr8MAAAA6TP///+/BgAAAOkp////vwUAAADpH////4sd/KIFCIXbD4S0/v//Mdu/ +CwAAAOkM////kI10JgBVieVWU4PsEItFCIsYi3AEhdt+KI22AAAAAI2/AAAAAA++BkaLFWCmBQiL +ShQ7ShhzEYgB/0IUS4Xbf+SDxBBbXl3DiRQkD7bAiUQkBOhGs///6+ONtgAAAACNvwAAAABVMcCJ +5YPsGItVCIld/Isahdt+H4lcJAiLQgTHBCQBAAAAiUQkBOiVPgAAOdgPlcAPtsCLXfyJ7F3DjbYA +AAAAVYnlg+woiV34odymBQgx24l1/IXAi3UIdAW7CAAAAIsNuKYFCIXJdA2LFZymBQiJ2AHQjVgB +jUX0iUQkDKH8pgUIiUQkCIsGxwQkAAAAAIlEJAToSPf//4tF9IsNzKYFCAHDhcl0OYtGFInCgeIA +8AAAgfoAgAAAdDmB+gBAAAB0LoH6AKAAAHQmgfoAEAAAdB6B+gDAAAB0Fo22AAAAAInYi3X8i134 +iexdw410JgBD6+1JdeqoSXTm6/SNdCYAVYnlV1ZTg+w8oSSnBQiLFZCmBQiJRdw50IlV1H4DiVXc +x0XsAAAAAItN1DlN7A+N2gAAAMdFyAAAAACLRciLPYimBQgB+IkEJOjy/v//iUXkMck7TdwPjfEB +AAChkKYFCIsVIKcFCIlF1IlV0I12AI0ESY0chQAAAACLRdCLNBiF9g+EvQEAAItV1I1xAQHKidCZ +9/6Jx4tF7Jn3/4tV5DnIicd0A4PCAotN0ItMGQiLBLmJTcA5wn42idEpwYnIi03QAUQZBItFwIkU +uKEQpwUIixWQpgUIOUQZBIlV1A+cwA+2wIkEGZCNtCYAAAAAO3XcifEPjHX/////ReyLRdSDRch4 +OUXsD4wt////i1Xcg/oBD44eAQAAiw0gpwUIjQRSjUSB9IlN0IsIhcl1CUqD6AyD+gF/8YtN0I0E +Uo1EgfSJRfCJ0YtF1Jn3+YXSiUXgdARAiUXgx0XoAAAAAItF4DlF6A+NsQAAAMdFxAAAAACLVeDB +4AQp0IlFzMdF2AAAAACLTegx/4t1xIlN7KGIpgUIjRz1AAAAAAHYiQQk6Jr5//+hiKYFCAHDiRwk +6Iv9//+JReSLVfCLTdiLQgiLVeABVeyLBIhBiU3Yi03MAc6LTew7DZCmBQh9GYtV5I0cOIlcJASN +BDqJ34kEJOg7BAAA65mhYKYFCItQFDtQGHMhxgIK/0AU/0Xoi03gg0XEDzlN6A+MYf///4PEPFte +X13DiQQkugoAAACJVCQE6AKw///r0qEgpwUIiUXQ6fT+//+NcQHpo/7//4sNkKYFCIlN1Omg/v// +kI10JgBVieVXVlOD7CyhJKcFCIlF4KGQpgUIOUXgfgOJReDHRewAAAAAOUXsD43iAAAAx0XUAAAA +AI10JgCNvCcAAAAAixWIpgUIi0XUAdCJBCTojfz//8dF0AAAAACLVeA5VdCJRegPjZEAAAChIKcF +CIlF2OsNkJCQkJCQkJCQkJCQkItV0I0EUo0chQAAAACLRdiLBBiFwA+E5QEAAItF7InWRotN6Jn3 +/jtV0InXdAODwQKLRdiLRBgIiUXQiwS4OcF+LonKKcKJ0ItV2AFEGgSLRdCJDLihEKcFCDlEGgQP +nMAPtsCJBBqJ9o28JwAAAACJddCLReA5xnyG/0Xsg0XUeItV7DsVkKYFCA+MMP///4t14IP+AQ+O +UgEAAKEgpwUIiUXYi1XYjQR2jUSC9In2iziF/3UJToPoDIP+AX/xx0XkAAAAAItV2I0Edo1EgvSJ +RfChiKYFCIkEJOiA9///oYimBQiJBCToc/v//4lF6ItV8MdF7AEAAACLQgiLCLgBAAAAOwWQpgUI +fX2/eAAAAOsNkJCQkJCQkJCQkJCQkItF7Jn3/oXSiVXcD4WcAAAAoWCmBQiLUBQ7UBhzecYCCv9A +FMdF5AAAAAChiKYFCAH4iQQk6Ab3//+hiKYFCAH4g8d4iQQk6PT6//+JReiLVfD/ReyLQgiLVdyL +DJCLRew7BZCmBQh8l6FgpgUIi1AUO1AYcw7GAgr/QBSDxCxbXl9dw4kEJLoKAAAAiVQkBOijrf// +6+WJBCS7CgAAAIlcJATokK3//+l3////i13ki0XkAcuJXCQEi03oAciJBCToagEAAIld5Old//// +oSCnBQiJRdjpxP7//5CNdCYAi1XQjXIB6WX+//+QjXQmAFWJ5Vcx/1ZTg+wMMds7HZCmBQh8LqFg +pgUIi1AUO1AYcw7GAgr/QBSDxAxbXl9dw4kEJLkKAAAAiUwkBOgHrf//6+WJffCLFYimBQiJ2MHg +BCnYjXMBjQTCiQQk6O/5//8Bxzs1kKYFCH0Dg8cCi03whcl0Kzs9EKcFCHwjoWCmBQiLUBQ7UBgP +g5gAAADGAgr/QBSLTfApz420JgAAAACLFYimBQiJ2MHgBCnYjQTCiQQk6Jj1//+hkKYFCDnGfSuh +YKYFCItQFDtQGHNFxgIs/0AUoWCmBQiLUBQ7UBhzH8YCIP9AFKGQpgUIOcaJ8w+MTf///+ka//// +kI10JgCJBCS7IAAAAIlcJAToN6z//+vUiQQkuiwAAACJVCQE6CSs///rrokEJLoKAAAAiVQkBOgR +rP//6Vj///+NdCYAVYnlV1ZTg+wci10Ii30MOft9WY22AAAAAI28JwAAAACLDQSnBQiFyQ+OhQAA +AIn4jXMBmff5iUXwifCZ9/k5RfCJRex+QaFgpgUIi1AUO1AYcyHGAgn/QBSLNQSnBQiJ2Jn3/inW +AfM5+3y0g8QcW15fXcOJBCS+CQAAAIl0JATog6v//+vSoWCmBQiLUBQ7UBhzDMYCIP9AFInz68qJ +9okEJLsgAAAAiVwkBOhXq///6+eNcwHrz5CNtCYAAAAAVYnli00IVot1DFOLXRCJ8g+2BjwudEuE +wHQQkI10JgCIAUJBD7YChMB19TnydhGAev8vdAvGAS9BjbQmAAAAAA+2A4TAdBSJ9o28JwAAAACI +AUNBD7YDhMB19cYBAFteXcOAfgEAdNjrrY22AAAAAFW6VlVVVYnlV1ZTg+wciw0QpwUIx0XwAAAA +AInI9+qJyMH4HynChdKJFSSnBQh1CrgBAAAAoySnBQihIKcFCIXAD4SSAAAAMfY7NSSnBQh9b8dF +7AQAAAAx/8dF6AAAAADrDZCQkJCQkJCQkJCQkJCLReiLHSCnBQiDwAOJRDsEi0XwxwQ7AQAAAIXA +dTox0jnyfxiLHSCnBQiLRDsIifbHBJADAAAAQjnyfvSDRegDRoPHDINF7AQ7NSSnBQh8sIPEHFte +X13Di0XsiQQk6J1XAACJRDsI67WhJKcFCI0EQMHgAokEJOiEVwAAoyCnBQjHRfABAAAA6Ur///+N +dgBVieVTg+wUi10Ihdt0QscEJAAAAAC4BQAAAIlEJAi4oG0FCIlEJAToPKr//4lEJASLFeypBQih +ZKYFCIlUJAiJBCToAar//4kcJOhJrf//kMcEJAAAAAC4BQAAAIlEJAi44G0FCIlEJATo+qn//4kE +JIsV7KkFCIlUJAToCKz//8cEJAAAAAC5IG4FCLgFAAAAiUwkBIlEJAjoyqn//4kEJIsVYKYFCIlU +JAToeKn//8cEJAAAAAC6BQAAALjAbgUIiVQkCIlEJATomqn//4kEJIsVYKYFCIlUJAToSKn//8cE +JAAAAAC4BQAAAIlEJAi4IG8FCIlEJAToaqn//4kEJIsVYKYFCIlUJAToGKn//8cEJAAAAAC4BQAA +AIlEJAi4QHAFCIlEJAToOqn//4kEJIsVYKYFCIlUJATo6Kj//8cEJAAAAAC54HEFCLgFAAAAiUwk +BIlEJAjoCqn//4kEJIsVYKYFCIlUJATouKj//8cEJAAAAAC6BQAAALigcwUIiVQkCIlEJATo2qj/ +/4kEJIsVYKYFCIlUJAToiKj//8cEJAAAAAC4BQAAAIlEJAi4AHUFCIlEJAToqqj//4kEJIsVYKYF +CIlUJAToWKj//8cEJAAAAAC4BQAAAIlEJAi4QHcFCIlEJAToeqj//4kEJIsVYKYFCIlUJAToKKj/ +/8cEJAAAAAC5oHgFCLgFAAAAiUwkBIlEJAjoSqj//4kEJIsVYKYFCIlUJATo+Kf//8cEJAAAAAC6 +BQAAALggegUIiVQkCIlEJAToGqj//4kEJIsVYKYFCIlUJAToyKf//8cEJAAAAAC4BQAAAIlEJAi4 +gHsFCIlEJATo6qf//4kEJIsVYKYFCIlUJATomKf//8cEJAAAAAC4BQAAAIlEJAi4QH0FCIlEJATo +uqf//4kEJIsVYKYFCIlUJAToaKf//8cEJAAAAAC5AH4FCLgFAAAAiUwkBIlEJAjoiqf//4kEJIsV +YKYFCIlUJAToOKf//8cEJAAAAAC6BQAAALjgfwUIiVQkCIlEJAToWqf//4kEJIsVYKYFCIlUJATo +CKf//8cEJAAAAAC4BQAAAIlEJAi4YIIFCIlEJAToKqf//4kEJIsVYKYFCIlUJATo2Kb//8cEJAAA +AAC4BQAAAIlEJAi4wIMFCIlEJATo+qb//4kEJIsVYKYFCIlUJAToqKb//8cEJAAAAAC54IQFCLgF +AAAAiUwkBIlEJAjoyqb//4kEJIsVYKYFCIlUJAToeKb//8cEJAAAAAC6BQAAALgghQUIiVQkCIlE +JATomqb//4kEJIsVYKYFCIlUJAToSKb//8cEJAAAAAC4BQAAAIlEJAi4YIUFCIlEJAToaqb//4kE +JIsVYKYFCIlUJAToGKb//8cEJAAAAAC4BQAAAIlEJAi4AIYFCIlEJAToOqb//4kEJIsVYKYFCIlU +JATo6KX//8cEJAAAAAC4BQAAALlPXgUIiUQkCIlMJAToCqb//4kEJLpmXgUIiVQkBOgZqP//6dP7 +//+QkJCQkJCQkJCQkJBVMdKJ5YPsCItFDItAECUA8AAAPQCgAAB0EYtFCIkEJOizpv//hcCJwngG +ieyJ0F3D6BKm//+LAIP4JnQKg/hfuv////915DHS6+CQjXQmAFWJ5VdWU4PsHItFDItdCIt1EIlF +8LgAgAAAiUQkBIkcJOgiqf//hcCJxw+EEAIAAIlEJAi4AIAAAIlEJASLRfCJBCToAKj//4XAD4TA +AAAA6KOl//+JReiLAIP4JolF7HRxg/hfdGyJPCToyaX//4l0JASLRfCJBCToqqX//4tF8IkEJOjn +QwAAxwQkAAAAAInDuAUAAACJRCQIuDOHBQiJRCQE6O+k//+JRCQIiVwkDItF7IlEJATHBCQAAAAA +6BSp//+6/////4PEHInQW15fXcOJPCTo7aP//4k8JInD6FOl//+D+wN1hYl0JASLRfCJBCToL6X/ +/zHShcB0yotF6IsAiUXs6XL///+NdCYAiTwk6CCl///3xgAOAAAPhcwAAACB5gDwAACB/gBAAAB0 +BDHS65OJHCS+AEAAAIl0JAToAaj//4XAicd0f4lEJAi5AEAAAIlMJASLRfCJBCTo46b//4XAdQqJ +PCTox6T//+u/i0XwiQQk6PJCAADHBCQAAAAAica6BQAAAIlUJAi4M4cFCIlEJATo+qP//4nD6FOk +//+JdCQMiVwkCIsAxwQkAAAAAIlEJAToGaj//4k8JOhxpP//6fj+//+JHCTonEIAAInD6B2k//+J +XCQMu06HBQiJXCQIiwDpxf7//5CJdCQEi0XwiQQk6Cmk//+FwA+EHf///4tF8L8zhwUIiQQk6FlC +AACJfCQEica4BQAAAIlEJAjHBCQAAAAA6Gaj//+Jw+i/o///iXQkDOulkOizo///iceLAIP4JnQF +g/hfdRWJdQyLRfCJRQiDxBxbXl9d6SgAAACJHCToAEIAAIlEJAy4TocFCIlEJAiLB+kw/v//jbYA +AAAAjb8AAAAAVYnlV1ZTg+xMoVGHBQiLXQyJRcihVYcFCPbHAYlFzKFZhwUIiUXQoV2HBQiJRdSh +YYcFCIlF2A+2BWWHBQiIRdx0BMZFy3KE23kExkXMd/bDQHQExkXNePbDIHQExkXScvbDEHQExkXT +d/bDCHQExkXUePbDBHQExkXZcvbDAnQExkXad/bDAXQExkXbeI1FyIkEJOh/pf//hcCJxg+EVAEA +AIlEJAi4AIAAAIlEJASLRQiJBCTo/aT//4XAD4SRAAAA6KCi//+Jx4sAiTQkiUXE6NGi//+LB4P4 +X3Rag/gmdFWLRQiJBCTo8kAAAMcEJAAAAACJw7gFAAAAiUQkCLhmhwUIiUQkBOj6of//iUQkCItF +xIlcJAyJRCQExwQkAAAAAOgfpv//uv////+DxEyJ0FteX13DiVwkBItFCIkEJOhRov//MdKFwHTh +iz+JfcTrj4k0JOhMov//9scOdWKB4wDwAACB+wBAAAB0BDHS67uLRQiJBCToaqX//4XAdO2LRQiJ +BCToU0AAALkFAAAAumaHBQiJxolMJAiJVCQExwQkAAAAAOhbof//icPotKH//4l0JAyJXCQIiwDp +Vv///4lcJASLRQiJBCToxqH//4XAdIuLRQi/BQAAALszhwUIiQQk6PU/AACJfCQIicaJXCQE66qL +RQiJBCTo3j8AAInD6F+h//+JXCQMuk6HBQiJVCQI66SQkJCQkJCQkFWJ5VdWU4PsDItdEInYS4XA +dQ2LRQiDxAxbXl9dw4n26BOl//+Jx4n2jbwnAAAAAItFDA+2FAOLBw+28vZEcAEBdB3oIKD//4sA +D7YEsItVCIgEE4nYS4XAddXrto12AIjQ6+qNtgAAAACNvwAAAABVieVXVlOD7AyLXRCJ2EuFwHUN +i0UIg8QMW15fXcOJ9uijpP//iceJ9o28JwAAAACLRQwPthQDiwcPtvL2RHABAnQd6ICi//+LAA+2 +BLCLVQiIBBOJ2EuFwHXV67aNdgCI0OvqjbYAAAAAjb8AAAAAVTHJieVXVlOB7OwEAACLRRSJjZD7 +//+LVQiLTRSLQAiJlYz7//+LSSgx0omFmPv//4P4DImVhPv//4mNlPv//w+O6hYAAIPoDImFmPv/ +/4tFEImFiPv//w+2AITAD4StAAAAiMExwDHbMfaJhYD7//+//////w++wYmdaPv//zHbg/g/ib1w ++///ibVs+///D496FgAAg/gmfSWD+CMPj/gBAACD+CB9F4PoCIP4BQ+HiwAAAI10JgCNvCcAAAAA +i5WQ+///i0UMKdAx0oP4AXZei72M+///hf90D4uFjPv//4gIQImFjPv///+FkPv///+FiPv//4uV +iPv//w+2AoTAiMEPhVv///+NtgAAAACLlYz7//+F0nQQi0UMhcB0CYuNjPv//8YBAIuVkPv//4HE +7AQAAInQW15fXcONdgCLtYT7//8x26GcqAUIixWgqAUIhfaJhaD7//+JlaT7//8PhBIBAACLvYT7 +//+LjYj7//8pz410JgCNvCcAAAAAxwQkAAAAAIu1iPv//4n4KdiNlaD7//+JVCQMAd6JRCQIiXQk +BOjun///hcB0JoP4/g+EswAAAIP4/w+EpAAAAI2NoPv//wHDiQwk6Aii//+FwHSsid6LlZD7///3 +1onwwegfSCHwjTwYi0UMKdAx0jnHD4M4////i4WM+///hcB0R4X2fiGJdCQIuCAAAACJRCQEi4WM ++///iQQk6Ieh//8BtYz7//+JXCQIi5WI+///i42M+///iVQkBIkMJOj1n///AZ2M+///Ab2Q+/// +i4WI+///jVwD/4mdiPv//+mN/v//Q+lq////iTQk6Mae//8Bw+lb////i5WI+///iRQk6LGe//+L +jYj7//+NRAgBiYWE+///6cv+//+D+CUPhZv+//+NdCYAjbwnAAAAAP+FiPv//4uViPv//w+2Cg++ +0YjIg/owdBeD+jAPjyEUAACD+iMPhA4UAACD+i11Cw++wImFgPv//+vFD77Ag+gwg/gJd1gxwImF +cPv//4G9cPv//8zMzAwPj8sTAACBvXD7///MzMwMD4SsEwAAi4Vw+///jRSAD77BjVRQ0ImVcPv/ +//+FiPv//4uViPv//w+2Cg++wYPoMIP4CXawD77Bg/hFD4RWEwAAMfaD+E+JtXz7//8PhEUTAAAP +vtGD+nqJlWT7//8Ph7MAAAD/JJWchwUIi518+///hdsPhZ4AAACLnXD7//+LlZD7//9LidjB6B9I +IdiNcAGLRQwp0DHSOcYPg3f9//+LhYz7//+FwHRGhdt+M4O9gPv//zB0RIlcJAiLlYz7//+4IAAA +AIlEJASJFCTovZ///wGdjPv//4uFiPv//w+2CIuVjPv//4gKQomVjPv//wG1kPv//+nd/P//iVwk +CIuNjPv//7gwAAAAiUQkBIkMJOu6jXQmAIuViPv//74BAAAAgDoldA+LhYj7//9GKfCAeAEldfGL +nXD7//+LlZD7//8p84nYwegfSCHYjTwwi0UMKdAx0jnHD4O5/P//i4WM+///hcB0XYXbfiqDvYD7 +//8wdHyJXCQIuCAAAACJRCQEi4WM+///iQQk6P+e//8BnYz7//+LjWj7//+FyXQwiXQkCIuFiPv/ +/ynwQIlEJASLlYz7//+JFCTouPr//wG1jPv//wG9kPv//+kI/P//iXQkCIuFiPv//4uNjPv//ynw +QIlEJASJDCToMJ3//+vOiVwkCIuNjPv//7gwAAAAiUQkBIkMJOuC/42I+///6QP///+LvXz7//+F +/w+F9f7//4XbdBO+AQAAADHbibVo+///iZ1s+///xoWc+///JYudfPv//42Fnfv//4XbdBMPto18 ++///jYWe+///iI2d+///D7aVZPv//4292Pv//8ZAAQCIEItNFI2FnPv//4lEJAiJPCSJTCQMuQAE +AACJTCQE6Beb//+FwInGdQ2Avdj7//8AD4X6AAAAi51w+///i5WQ+///KfOJ2MHoH0gh2I0EMImF +XPv//4tFDCnQMdI5hVz7//8Pgzr7//+LhYz7//+FwHRihdt+LoO9gPv//zAPhJIAAACJXCQIi5WM ++///uCAAAACJRCQEiRQk6Hyd//8BnYz7//+LhWz7//+FwHVPi4Vo+///hcB0LYl0JAiLhYz7//+J +fCQEiQQk6DT5//8BtYz7//+LjVz7//8BjZD7///pfvr//4l0JAiLlYz7//+JfCQEiRQk6K+b///r +0Yl0JAiLjYz7//+JfCQEiQwk6H/4///ruYlcJAi4MAAAAIlEJASLhYz7//+JBCTpaf///zHS6Wv6 +//+DvXz7//9PD4Re/f//g718+///RQ+Ec/7//4tFFIu9cPv//4tIFIHBbAcAAIP/AQ+MggIAAInI +uh+F61H36onIwfgfwfoFKcKNBJKNBIDB4AIpwcHpHynKidGDvXz7//9PD4RDAgAAicqJy8HqH4mV +ePv//4115HQC99u5zczMzInYTvfhweoDjQSSAcApw4jYBDCJ04XSiAZ15YuNePv//4XJdAROxgYt +g72A+///LQ+E5gEAAI1V2InwKdCJlUD7//+NXDj0hdsPjoYAAACDvYD7//9fD4RZAQAAi5WQ+/// +i0UMKdAx0jnHD4N6+f//i4V4+///hcB0IYuFjPv//0aFwHQQi4WM+///xgAtQImFjPv///+FkPv/ +/4uFjPv//4XAdCGJXCQIi5WM+///vzAAAACJfCQEiRQk6KKb//8BnYz7//8BnZD7//8xyYmNcPv/ +/ym1QPv//4udcPv//4uVkPv//4u9QPv//4PHDCn7idjB6B9IIdiNBDiJhVj7//+LRQwp0DHSOYVY ++///D4PV+P//i4WM+///hcB0VIXbfiqDvYD7//8wdHCJXCQIi5WM+///uCAAAACJRCQEiRQk6Bub +//8BnYz7//+LhWj7//+FwHQtiXwkCIuNjPv//4l0JASJDCTo3fb//wG9jPv//4uVWPv//wGVkPv/ +/+kn+P//iXwkCIuFjPv//4l0JASJBCToWJn//+vRiVwkCLgwAAAAiUQkBIuFjPv//4kEJOuOi5WQ ++///i0UMKdAx0jnDD4Mh+P//i72M+///hf90IYlcJAi5IAAAAIlMJASLjYz7//+JDCTodJr//wGd +jPv//wGdkPv//zHAi5Vw+///Kdo5nXD7//8PnsBIIdCJhXD7///puf7//41N2ImNQPv//+mr/v// +hckPidv7///psP3//78BAAAA6XT9//++gYcFCIm1dPv//4uFfPv//4XAD4WR+v//xwQkAAAAAItN +HItVFItFGIu1jPv//4lMJBSLjXT7//+JVCQMiUQkEIlMJAi5/////4lMJAToJ/b//4udcPv//4nH +i5WQ+///KcOJ2MHoH0gh2I0EOImFYPv//4tFDCnQMdI5hWD7//8PgyP3//+LhYz7//+FwHR0hdt+ +LoO9gPv//zAPhL4AAACJXCQIi5WM+///uSAAAACJTCQEiRQk6GWZ//8BnYz7//+LRRiLVRSLTRyJ +RCQQi0UMiVQkDIuVkPv//4lMJBSLjXT7//8p0IlEJASLhYz7//+JTCQIiQQk6Hv1//8BvYz7//+L +vWj7//+LlWD7//8BlZD7//+F/w+ETPb//zu1jPv//w+DQPb//+iimf//iccPthaLBw+22vZEWAEC +dBvojJf//4sAD7YEmIgGRju1jPv//3Lb6Q/2//+I0OvsiVwkCLgwAAAAiUQkBIuFjPv//4kEJOk9 +////uIqHBQiJhXT7///pgP7//4O9fPv//0UPhBL5//+LvXD7//+D/wJ8C4tFFItICOnr+///vwIA +AADr7pCNdCYAg718+///RQ+E4/j//4u9cPv//4P/AnwLi42Y+///6bz7//+/AgAAAOvujbYAAAAA +g718+///RQ+Es/j//4u9cPv//4P/AnwLi1UUi0oE6Yz7//+/AgAAAOvujbYAAAAAg718+///RQ+E +g/j//4O9cPv///+LTRx0JoudcPv//4P7CH8bvmdmZmaJyEP37onIwfgfwfoCidEpwYP7CH7qi71w ++///g/8JD40x+///vwkAAADpJ/v//7gBAAAAiYVs+///uHAAAACJhWT7//+F2w+EPPn//zHAiYVo ++///uAEAAACJhWz7///pJPn//7hyXQUIiYV0+///6WH9//+DvXz7//9FD4Tl9///i71w+///g/8C +fAqLVRSLCum/+v//vwIAAADr77mThwUIiY10+///6Sj9//+DvXz7//9FD4Ss9///i71w+///g/8C +fCmLTRSLURiLQRwp0I1YB7qTJEmSidj36o0ME4nYwfkCwfgfKcHpZ/r//78CAAAA69CQg718+/// +RQ+EY/f//4u9cPv//4P/Anw6i1UUvpMkSZKLWhiDwwaJ2PfuidjB+B8B2sH6AinCjQTVAAAAACnQ +i1UUKcOLQhwp2I1YB4nY9+7rlL8CAAAA67+NtCYAAAAAg718+///Tw+FJfj//+n+9v//g718+/// +RQ+EE/j//4O9fPv//08PhOT2//+LvXD7//+D/wF8EYtFFItIFIHBbAcAAOm3+f//vwEAAADr6JCF +23QTMf++AQAAAIm9aPv//4m1bPv//4udlPv//4XbD4TzAAAAi5WU+///iRQk6JWT//+LnXD7//+J +xouVkPv//ynDidjB6B9IIdiNPDCLRQwp0DHSOccPg2Pz//+LhYz7//+FwA+EA/f//4Xbfi6DvYD7 +//8wD4SBAAAAiVwkCLggAAAAiUQkBIuFjPv//4kEJOihlf//AZ2M+///i4Vs+///hcB1NYudaPv/ +/4XbdA+JdCQIi4WU+///6Zb2//+JdCQIi42U+///i4WM+///iUwkBIkEJOm39v//iXQkCIuVlPv/ +/4uNjPv//4lUJASJDCTouPD//+lr9v//iVwkCIuNjPv//7gwAAAAiUQkBIkMJOl6////uUVcBQiJ +jZT7///p/f7//4O9fPv//0UPhIv1//+LvXD7//+D/wJ8C4tVFItKDOlk+P//vwIAAADr7oO9fPv/ +/0UPhGH1//+LvXD7//+D/wJ8MItFFItIDIO9gPv//zAPhDL4//+DvYD7//8tD4Ql+P//u18AAACJ +nYD7///pFfj//78CAAAA68mDvXz7//9FD4QS9f//i1UUi3oUi1oci1IYjYdsBwAAiYVU+///idgp +0ImVTPv//42wfgEAAInwupMkSZL36onwwfgfAfLB+gIpwo0E1QAAAAAp0CnGidgp8IPAA4mFUPv/ +/w+IsAEAADHA9oVU+///A4mFRPv//3Vli4VU+///vh+F61H37ouFVPv//4nXwfoFiZU0+///mSmV +NPv//4uFNPv//40EgI0EgImFKPv//8HgAjmFVPv//3UYifjB+Acp0I0EgI0EgMHgBDmFVPv//3UL +vwEAAACJvUT7//+LtUT7//+LlUz7//8p84HrbQEAAInYKdCNsH4BAAC4kyRJkvfuifDB+B+NFDLB ++gIpwo0E1QAAAAAp0CnGKfOJ2IPAA3gM/4VU+///iYVQ+///D77Bg/hHD4S/AAAAg/hndD2LvXD7 +//+D/wJ8K4uFUPv//7qTJEmS9+qLhVD7//8BwouFUPv//8H6AsH4HynCjUoB6Zf2//+/AgAAAOvO +i71w+///g/8CfGuLhVT7//+7H4XrUffri4VU+///iZUs+///i40s+///mcH5BSnRjQSJjQSAweAC +KYVU+///i7VU+///g8ZkifCJ8ffriZUs+///i50s+///iYUo+///ifCZwfsFKdONBJuNBIDB4ALp +rPv//78CAAAA646LvXD7//+D/wF8C4uNVPv//+n99f//vwEAAADr7oHHawcAADHAib1U+///9oVU ++///A4mFSPv//3Vhifi+H4XrUffui4VU+///idfB+gWJlTT7//+ZKZU0+///i4U0+///jQSAjQSA +iYUo+///weACOYVU+///dRiJ+MH4BynQjQSAjQSAweAEOYVU+///dQu+AQAAAIm1SPv//4uVSPv/ +/42cGm0BAACLlUz7//+J2CnQjbB+AQAAuJMkSZL37onwwfgfjRQywfoCKcKNBNUAAAAAKdApxinz +g8MDiZ1Q+///6U7+//+F23QTuAEAAACJhWj7//8xwImFbPv//4uFfPv//4XAD4Q68///6RPy//+D +vXz7//9FD4QG8v//i71w+///g/8DfQW/AwAAAItFFItIHEHp2fT//4O9fPv//0UPhN3x//+LvXD7 +//+D/wJ8C4tVFItKCOl3/P//vwIAA +ADr7oO9fPv//0UPhLPx//+LvXD7//+D/wJ8C4uNmPv//+lN +/P//vwIAAADr7o22AAAAAIO9fPv//0UPhIPx//+LvXD7//+D/wJ8C4tFFItIEOl9////vwIAAADr +7o22AAAAAIudcPv//zHSi72Q+///S4nYwegfSCHYjXABi0UMKfg5xg+DOe7//4uNjPv//4XJD4QE +8f//hdt+KoO9gPv//zB0NolcJAiLjYz7//+4IAAAAIlEJASJDCToe5D//wGdjPv//4uFjPv//8YA +CkCJhYz7///p wfD//4lcJAi6MAAAAIlUJASLlYz7//+JFCTryPy4CwAAAIt1FInBjb2o+///jYWo ++///86WJBCS7Z2ZmZo115OjHSAAAicHB6B+JhXj7//+JyIu9ePv///fricjB+B/B+gIpwo0EkgHA +KcGF/4nIidF0AvfYTgQwhdKIBr8BAAAAdc3plPP//4udcPv//4uVkPv//0uJ2MHoH0gh2I1wAYtF +DCnQMdI5xg+DPe3//4uFjPv//4XAD4QI8P//hdt+KoO9gPv//zB0L4lcJAi5IAAAAIlMJASLjYz7 +//+JDCTof4///wGdjPv//4uFjPv//8YACen//v//iVwkCIuVjPv//78wAAAAiXwkBIkUJOvPi71w ++///g/8BfDCLVRS4kyRJkotaGIPDBvfridjB+B+NFBrB+gIpwo0E1QAAAAAp0CnDjUsB6Y/y//+/ +AQAAAOvJg718+///RQ+EjO///4u9cPv//4P/AXwLi1UUi0oY6WXy//+/AQAAAOvug718+///RQ+E +hPD//4u9cPv//4P/Anw3i1UUux+F61GLchSJ8PfrifDB+gWJlSj7//+ZKZUo+///i5Uo+///jQSS +jQSAweACKcbpxPv//78CAAAA68KLVRSLSiCFyQ+IzOv//4t6JIX/D4jqAAAAi51w+///i5WQ+/// +S4nYwegfSCHYjXABi0UMKdAx0jnGD4PZ6///i4WM+///hcB0QoXbfi6DvYD7//8wD4SNAAAAiVwk +CIuFjPv//7kgAAAAiUwkBIkEJOgbjv//AZ2M+///i5WM+///xgIrQomVjPv//wG1kPv//4n4uYmI +iIj36Yn4wfgfjTQXi71w+///wf4FKcaD/wR9Bb8EAAAAifD36YnwwfgfAfLB+gUpwonQjRySweAE +KdCNHJuNFIUAAAAAifAp0I0MmOkg8f//iVwkCIuNjPv//7gwAAAAiUQkBIkMJOlu////i51w+/// +i5WQ+///S4nYwegfSCHYjXABi0UMKdAx0jnGD4Pv6v//i4WM+///hcB0PoXbfiqDvYD7//8wdD6J +XCQIi4WM+///uSAAAACJTCQEiQQk6DWN//8BnYz7//+LlYz7///GAi1CiZWM+///AbWQ+///99/p +E////4lcJAiLjYz7//+4MAAAAIlEJASJDCTrwIn2/4WI+///iYV8+///i4WI+///D7YI6aHs//8P +vsGD6DCD+AcPjkXs//+/////f4m9cPv//+lL7P//uwEAAADpvev//4P6XnQIg/pf6d7r//+4AQAA +AImFaPv//+mg6///g/hBD4wn6v//g/hfD46e6f//g+hhg/gd6YLp//+NdgCLhZj7//+FwA+FEen/ +/7gMAAAA6QHp//+QkJCQkJCQkFWJ5YtFCF2LQAjDkI10JgBVieWLRQhdi0AMw5CNdCYAVYnli0UI +XYtAEMOQjXQmAFWJ5YtFCFYx9lOLCItYBDnZc0SNtgAAAACNvCcAAAAAiwGFwHQqi0EEugEAAACF +wHQWjbQmAAAAAI28JwAAAACLQARChcB1+DnydgSJ1on2g8EIOdlyyVuJ8F5dw412AFUxyYnlV4t9 +CFYx9lOLF4tfBDnacymNdgCNvCcAAAAAiwKFwHQSi0IERkGFwHQJkItABEGFwHX4g8IIOdpy4Tt3 +DHQHMcBbXl9dwztPEHX0uAEAAADr74n2jbwnAAAAAFWJ5VdWU4PsHItdCIkcJOgc////iRwkicfo +8v7//4kcJInG6Pj+//+JRfCJHCToDf///4l8JAiJw7iciQUIiUQkBItFDIkEJOhbh///iXQkCLiz +iQUIiUQkBItFDIkEJOhDh///i0XwMdJSMdJQi0Xw3ywkg8QIUlbfLCTZyYPECNgNIIoFCIlEJAi4 +AIoFCIlEJASLRQze8YkEJN1cJAzoA4f//4lcJAi4yokFCIlEJASLRQyJBCTo64b//4PEHFteX13D +jXQmAI28JwAAAABVieVXVlOD7AyLdQiLfQyLRgiJPCSJRCQE/1YYixaNBMI7RgRzP4sQhdJ0K4XA +icN0JY20JgAAAACNvCcAAAAAiwOJPCSJRCQE/1YchMB1EYtbBIXbdekxwIPEDFteX13DiwPr9Ohu +h///jbYAAAAAVYnlg+wIi0UIi0gQhcl0IYsQi0gEjXQmAI28JwAAAAA5ynMRiwKFwHUFg8II6/HJ +wzHA6/roLof//422AAAAAFWJ5VZTg+wQi10Ii3UMi0MIiTQkiUQkBP9TGIsTi0sEjRTCOcpzSoXS +idB0Do12ADkwdDKLQASFwHX1g8IIOcpzG420JgAAAACNvCcAAAAAiwKFwHUJg8IIOcpy8zHAg8QQ +W15dw4tABIXAdMqLAOvu6LKG//+NdgCNvCcAAAAAVTHJieVXVlOD7ASLRQiLfRCLGItABIlF8DnD +czONdgCLM4X2dB6J2pCNtCYAAAAAOflzHIsCi3UMi1IEiQSOQYXSdeyDwwg7XfBy1I10JgBbichb +Xl9dw5CNtCYAAAAAVYnlVzH/VlOD7AyLRQiLMItABDnGc0mJ9o28JwAAAACLFoXSdCuJ85CNtCYA +AAAAi0UQiUQkBIsDiQQk/1UMhMB0HYtbBEeF23Xli1UIi0IEg8YIOcZyyJCNtCYAAAAAg8QMifhb +Xl9dw422AAAAAFWJ5YtNCFaLdQxTMdsPthGE0nQojbYAAAAAjb8AAAAAidgPttLB4AUp2EEB0DHS +9/YPtgGEwInTiMJ15InYW15dw420JgAAAACNvCcAAAAAVbkDAAAAieVXi30IVr4JAAAAOf5Tczkx +0on49/GF0nQv6w2QkJCQkJCQkJCQkJCQQY00jkE5/nMXMdKJ+PfxhdJ17Y22AAAAAI28JwAAAABb +ifgx0vfxMcBeX12F0g+VwMONtCYAAAAAjbwnAAAAAFWJ5VOD7ASLXQiD+wl3BbsKAAAAg8sBifaN +vCcAAAAAiRwk6Gj///+EwHUFg8MC6+9ZidhbXcOJ9o28JwAAAABVoYiJBQiJ5YtVCIkCoYyJBQiJ +QgShkIkFCIlCCKGUiQUIiUIMoZiJBQiJQhBdw5BV2e6J5YtVCItKFNlBCNnA3eLf4J52edno3eHf +4N3ZnnZu2UEM3enf4J52ZNkB2cDd5N/g3dyeck3Zyd3j3+Dd2552TNlBBN3h3+CedjjZy93r3+Dd +2p5yJ9rp3+CedhK4AQAAAI10JgCNvCcAAAAAXcPHQhSIiQUIMcDr8412AN3Y3djr7N3YkI20JgAA +AADd2OvsjbYAAAAAjb8AAAAAVYnlV1ZTg+wci0UQi30Ii3UMhcAPhPYAAACLXRSF2w+E6wAAAMcE +JCgAAADop4P//4nDMcCF2w+EwAAAAIX2D4TWAAAAiXMUiRwk6BD///+EwA+ErQAAAIB+EAAPhbIA +AAAx0lJX3ywkg8QI2HYI2X3uD7dF7mYNAAxmiUXs2W3s333g2W3ui0XgiQQk6F7+//+JQwjB4AOJ +BCToOIP//4kDhcCJwnRdi0MIjQzCOcqJSwSJ0HMdicqNtCYAAAAAxwAAAAAAx0AEAAAAAIPACDnQ +cuzHQwwAAAAAi0UQx0MQAAAAAMdDJAAAAACJQxiLRRSJQxyLRRiJQyCJ2IPEHFteX13DiRwk6HuF +//+NdgAxwOvpifjpb////76IiQUI6SD///+NdCYAjbwnAAAAAFWJ5VdWU4PsDIt9CIs3i0cEOcZz +Uo10JgCNvCcAAAAAi xaF0nQ6i14Ehdt0ZYtPIIXJdVLHAwAAAACLUwSLRySF0olDBIlfJInTdeSF +yXUtx0YEAAAAAItHBMcGAAAAAIPGCDnGcrnHRwwAAAAAx0cQAAAAAIPEDFteX13DiwaJBCT/0evK +iwOJBCT/0YtPIOuii08g67WJ9o28JwAAAABVieVXVlOD7AyLRQiJRfCLcCCF9g+ExgAAAItYEIXb +D4S7AAAAiziLQAQ5x3Nwiw+FyXQkhf+J+3QejbYAAAAAiwOJBCSLTfD/USCLWwSF23Xui1Xwi0IE +g8cIOcdyz4tN8IsROcKJ13M0jbYAAAA Ai1 8Ehdt0IIn2jb +wnAAAAAItzBIkcJOgthP//hfaJ83Xv +i1Xwi0IEg8cIOcdy0otN8ItZJIXbdBmQjbQmAAAAAItzBIkcJOj9g///hfaJ83Xvi1XwiwKJBCTo + +6oP//4tN8IlNCIPEDFteX13p2IP//4tF8IsQi0AE6Xf///+NdgBVieWD7AiLTQiLQSSFwHQQicKL +QASJQSSJ7InQXcOJ9scEJAgAAADo7ID//4nC6+hVieWLRQiLVQyLSCTHAgAAAACJSgSJUCRdw422 +AAAAAFWJ5VdWU4PsDIt1CA+2RRSLfQyIRfOLRgiJPCSJRCQE/1YYixaNHMI7XgQPg6QAAACLRRCL +E4kYMcCF0nQ0iVQkBIk8JP9WHITAdEaAffMAizt0HItLBIXJdR/HAwAAAADrDZCQkJCQkJCQkJCQ +kJCJ+IPEDFteX13DiwGLUQSJA4lTBIlMJASJNCToUP///+vei0MEhcB0IY20JgAAAACLAIk8JIlE +JAT/VhyEwHUOi1sEi0MEhcB15jHA67SAffMAi1MEizp0p4tCBIlDBIlUJATrs+gegP//jbYAAAAA +VYnlV1ZTg+wsi1UIi0IgiUQkEItCHIlEJAyLQhiJRCQIi0IUiUQkBItFDIkEJOjd+///iccxwIX/ +D4S+AAAAi00Ii0EkixGJRySLQQSJVfA5wnN1jbYAAAAAjb8AAAAAi1XwiwqFyXRSidOQjXQmAIsL +i0cIiU3oiUQkBIkMJP9XGIsXjTTCO3cED4O3AAAAi0MEiUXsiwaFwA+EhQAAADtd8HRgi0YEiUME +iV4Ei13shdt1u4tVCItCBINF8Ag5RfBynItNCIsRiRQk6MeB//+LVQiLB4kCi0cEiUIEi0cIiUII +i0cMiUIMi0ckiUIkiTwk6KCB//+4AQAAAIPELFteX13DiTwk6MP9//+JwjHAhdJ06ItGBItN6IlC +BIkKiVYE64n/RwyLReg7XfCJBg+EeP///4lcJASJPCTovv3//+ln////6Mx+//+NdCYAVYnlg+wo +iV34i10MiXX8i3UIhdsPhBkBAACJXCQEMcmNRfSJTCQMiUQkCIk0JOid/f//hcB0Cotd+It1/Ins +XcOLRfSLEIXSD4W7AAAA/0YMMdKJGP9GEItGDFIx0lCLRgiLThTfLCSDxAhSUN8sJIPECNhJCNnJ +2unf4J53BInY67SJNCTolfn//4tGDDHSUjHSUItGCItOFN8sJIPECFJQ3ywkg8QI2UEI2cDYytnL +3evf4N3annZGgHkQAHQ53dnYSQzZffIPt0XyiTQkZg0ADGaJRfDZbfDffejZbfKLReiJRCQE6MT9 +//+EwA+UwA+2wEghw+uG2EkM3snrxd3Y3djpdv///4k0JOhv/P//icExwIXJD4Qa/////0YQi1X0 +iRmLQgSJQQSJSgTpTP///+iQff//kI20JgAAAABVuAEAAACJ5YPsKIlEJAyNRfSJXfiLXQiJRCQI +i0UMiXX8iRwkiUQkBOhi/P//icYxwIX2dDf/SxCLRfSLAIXAdSn/Swwx0otDDFIx0lCLQwiLSxTf +LCSDxAhSUN8sJIPECNgJ2unf4J53DInwi134i3X8iexdw4kcJOhh+P//i0MMMdJSMdJQi0MIi0sU +3ywkg8QIUlDfLCSDxAjZAdjJ3erf4N3ZnnY6gHkQAHQs2EkE2X3yD7dF8okcJGYNAAxmiUXw2W3w +333o2W3yi0XoiUQkBOiX/P//65DYSQTYSQjrz93Y64SQkJCQkJCQkJBVieWD7AjHBCQBAAAA6D7R +//+J7F3DjXYAjbwnAAAAAFWJ5VdWU4PsHDHbx0Xw/////4tFCMdF7AAAAACJBCTopHz//4lF6ItV +DIsChcAPhJ0AAADHReQAAAAAkI10JgCLVQiLReiJVCQEi1UMiUQkCIsEmokEJOj8fP//hcB1UItV +DIsEmokEJOhafP//O0Xoidp0X4N98P90ZYt9EIX/dCL8i3Xwi0UUi03ki30QD6/Gi3UQAc8BxotF +FDnAicHzpnQMx0XsAQAAAJCNdCYAi0UMQ4tVFAFV5IsUmIXSD4V7////i0Xsuv7///+FwHUDi1Xw +g8QcidBbXl9dw4n2iV3w68uNdCYAjbwnAAAAAFWJ5YPsKIN9EP+JXfiJdfx0frgFAAAAiUQkCLgk +igUIiUQkBMcEJAAAAADoeHr//8cEJAEAAACJxotFCIlEJAToCxkAAMcEJAAAAACJw4tFDIlEJAi4 +BQAAAIlEJAToXSMAAIlEJAwxwIlcJBCJdCQIiUQkBMcEJAAAAADoZ37//4td+It1/InsXcOQjXQm +ALoFAAAAuEGKBQiJVCQI64BVieVXVlOD7CyLRQjHReAAAAAAi1UMi00QiUXwuAUAAACJVeyJTeiJ +RCQIuFyKBQiJRCQExwQkAAAAAOjIef//iUQkBKFkpgUIiQQk6Jd5///HReQAAAAAi1XwiwKFwHRo +x0XYAAAAAItN7InDiU3ci33khf90F/yLddiLfeyLRegB94t14DnAicHzpnRyiVwkCKFkpgUIu3GK +BQiJXCQEiQQk6EB5//+LVdyJVeD/ReSLTegBTdiLVeQBTdyLTfCLBJGFwInDdaehZKYFCItQFDtQ +GHMOxgIK/0AUg8QsW15fXcPHRQwKAAAAiUUIg8QsW15fXel+eP//jbYAAAAAiVwkCKFkpgUIuXuK +BQiJTCQEiQQk6M54///rko10JgBVieWD7CiJXfSLRRCLXQyJdfiLdRiJffyLfRSJdCQMiUQkBIl8 +JAiJHCToIf3//4XAicJ4D4td9InQi3X4i338iexdw4lEJAiLRQiJXCQEiQQk6On9//+JdCQIi0UQ +iXwkBIkEJOh2/v///1Ucuv/////rwY22AAAAAI2/AAAAAFWJ5VdWU4PsCItFDMdF8AAAAACLXRSL +AIXAdDr8x0XsAAAAAI22AAAAAI2/AAAAAIt9EInZi0Xsi3UIAcc52/OmdB3/RfCLVQwBXeyLRfCL +BIKFwHXaMcCDxAhbXl9dw4tV8ItNDIsEkevtkJCQkJBVieWLTQgPtgGJyoTAdA2QPC90DEIPtgKE +wHX0XYnIw0IPtgI8L3T4hMB0BInR6+SAOS91541K/+vijbYAAAAAVYnlg+wIiV38i10IiRwk6NR4 +//+D+AF2B4B8GP8vdAeLXfyJ7F3DSIP4AXbzgHwY/y917OvxkJCQkJCQkJCQkFWJ5YtFCF2jpKgF +CMONdgBVMcCJ5YPsKIsVYKYFCIld+Il1/PYCIA+VwInDS3QSiRQk6GF2//+FwHR1ixVgpgUIiRQk +6G95//+FwHQH6IZ3//+LGIXbeFjHBCQAAAAAvgUAAAC5gooFCIl0JAiJTCQE6AJ3//+JxqGkqAUI +hcB0P4kEJOh3IQAAiUQkDLqOigUIoTCjBQiJdCQQiVQkCIlcJASJBCToDXv//5CNdCYAi134i3X8 +iexdw422AAAAAIl0JAy4TocFCIlEJAihMKMFCIlcJASJBCTo2nr//+vQVYnli0UIi1UM9sQIdAqA +egN4dDLGQgNT9sQEdAqAegZ4dB3GQgZT9sQCdAqAegl4dAbGQglUXcPGQgl06/iJ9sZCBnPr4cZC +A3PrzI10JgBVuGIAAACJ5YtVCIHiAPAAAIH6AGAAAHRTgfoAIAAAuGMAAAB0RoH6AEAAALhkAAAA +dDmB+gCAAAC4LQAAAHQsgfoAEAAAuHAAAAB0H4H6AKAAALhsAAAAdBIxwIH6AMAAAA+UwEiD4MyD +wHNdw4n2VYnlVlOD7BCLdQiLXQyJNCToev///4gD98YAAQAAsHJ1ArAtiEMBifKE0rB3eAKwLYhD +AvfGQAAAALB4dQKwLYhDA/fGIAAAALBydQKwLYhDBPfGEAAAALB3dQKwLYhDBffGCAAAALB4dQKw +LYhDBvfGBAAAALBydQKwLYhDB/fGAgAAALB3dQKwLYhDCPfGAQAAALB4dQKwLYhDCYldDIl1CIPE +EFteXemS/v//ifZVieWLRQiLQBCJRQhd6T7///+QkJCQkJCQkJCQkJCQkFWJ5Vcx/1ZTg+wMi10Q +i3UMhdt1C4PEDIn4W15fXcOQiVwkCItFCIl0JASJBCTofR8AAIP4/3TdhcB0CgHHAcYpw3Xc68/o +HXX//8cAHAAAAOvCkJCQkJCQkJCQkJCQkFUxwInlg+wYiUQkBItFCIld9LsBAAAAiXX4iX38iQQk +6LN0//+FwHQegDhDdQaAeAEAdBH8v5WKBQi5BgAAAInG86Z1AjHbidiLdfiLXfSLffyJ7F3DkJCQ +kJCQkJCQkFWJ5YPsSIld9ItdCIl1+Il9/IP7AdttDHRm2y3AigUI3enf4J52WdnA2zwkMfYx/9t9 +2OiIMAAAiUXQhduJVdTbbdh1W99t0ItF1IXAeEba6d/gnnoCdAi+AQAAADH/kItF0ItV1AHwEfpS +UN8sJIPECIXSeBeNdgCNvCcAAAAAi130i3X4i338iexdw9gF4IoFCOvr2AXgigUI67KNdgDd2Ou8 +jbYAAAAAjb8AAAAAVYnlV7//////VlOD7FyLRRSLXQiJBCTorHT//4lFtIt1DI1VuIlcJASJFCQB +84l0JAiNtCYAAAAAjbwnAAAAAOiDdf//i1UQD7YChMB0DDx+ifd3Aw+2+P9FEDn+cwKJ94l8JAgp +/o1FuAHwKfuJRCQEiRwk6E51//+F9nQYi1W0i0W0KdOJRCQIi1UUiRwkiVQkBOuqg8RcidhbXl9d +w1WJ5VdWU4HsrAAAAItNJMdFwP////+LRQiLVQyJTdyLdRiLTRSJReCLfRwxwIlV5IPhA4tVIPZF +FCCJTdSJVdgPlcDHRbBiXAUIx0WsAQAAAMdFpEVcBQhIg+DoBQAEAACJRdDoD3P//4nDiwCJRaCJ +BCTosHP//4nCjUD/g/gPdwmJVayLTaCJTbCLQwiLWwSJRaiJHCTojHP//4P4EHcDiV2ki1UQgcKH +AgAAOX3ciVW4iVW8D4eJBgAAcgk5ddgPh34GAACJNCSLRdiLVdyJfCQEiUQkCIlUJAzoHzQAAInR +CcF1eYk0JItF2ItV3Il8JASJRCQIiVQkDOjPMgAAiUQkCItd4ItF4IlUJAz3ZCQIidGJRciLRCQM +i1XID6/Yi0QkCIkUJAHZi13kD6/YAdmJTcyLTcyJTCQE6I4yAACLTeQx0YtV4DHQCcEPhAwDAACN +tgAAAACNvwAAAADfbdiLRdyFwA+I5wIAAN9t4ItF5IXAD4jOAgAAV1bfLCSDxAiF/w+IswIAAPZF +FBDe8t7J2cDbvXj///8PhGkCAADd2MdFwAAAAACLRdAx0lLZ6FDfLCSDxAiNtgAAAADbrXj////Z +ytjJ/0XA2cDYytnL3evf4N3annIGg33ACHze3djbrXj///++5IoFCItF1N7xiQQk2cDbvXj////b +fCQE6Kj8//+JdCQEi1UQ23wkCIkUJOgNdf//i00QiQwk6AJy//+Jw4nCi3Wsi0WsRoPAAvZFFCB1 +BotFrIPAAznYD4JyAQAA9kUUCHQSi0UQgHwC/zAPhF4BAACNdCYAiVwkCItNuItFECnZiU28iQwk +iUQkBOhNcf//i028Acsp8/ZFFAQPhQQBAAD2RRRAdF6LRcCFwHhri13Ahdt0IItFuP9FuPZFFCB1 +CIN9wAGya3QKi03AD7aRnIoFCIgQgH0UAHkq9kUUIHQRi03Ahcl0CotFuMYAaUCJRbiLVbjGAkJC +iVW4ifaNvCcAAAAAi024xgEAi0W8gcSsAAAAW15fXcPHRcAAAAAAMfaLTdyJtWz///+/AQAAADmN +bP///4m9aP///3NWi3XQMf+J9v9FwIN9wAgPhFz///+LhWj////35onBi4Vo////iY1o////D6/H +AcKLhWz///8Pr8aNHBA7XdyJnWz///9ywQ+HJP///ztN2HK26Rr///+LRdg5hWj///9yn+kK//// +kItFqItNpItVvIlEJAiLRbyJTCQMiRQkKcOJXCQE6K77//+JRbzp0v7//9st0IoFCLvqigUIi0XU +2614////iQQk3snbfCQE6NT6///bLdCKBQiJXCQEi0UQ3vmJBCTbfCQI6DFz//+LVRCJFCToJnD/ +/4nDMfbpVf7//9t8JASLVdS/6ooFCIkUJOiR+v//iXwkBItNENt8JAiJDCTo9nL//4tFEIkEJOjr +b///68PYBeCKBQjpQv3//9gF4IoFCOkn/f//2AXgigUI6Q79///HRcQAAAAAx0W0AAAAAPZFFBAP +hFsBAADHRcAAAAAAMdKLRdA7VcwPh0YBAAByCTtFyA+HOwEAAInGideJdCQIi03Mi1XIiXwkDIlM +JASJFCToSjAAAIl0JAiJwYtVxIl8JAzB4QIBwY0ESjHS93XQiYV0////i0W00fiNHFCLRciLVcyJ +BCSJVCQE6OAuAAA7XdCJVcyLlXT///+JRciJVcQPgygCAACLTbSNBBmFwA+VwA+2wP9FwDHSO1XM +iUW0i0XQdxFyBTtFyHcKg33ACA+MZf///4N9zAAPh5IAAACDfcgJD4eIAAAAg33UAQ+EvQEAAItF +1IXAdSGLfbSF/34ax0W0AAAAAIuNdP///0GJTcSD+QoPhIEBAACDfcwAd1CDfcgJd0qLdcSF9nUG +9kUUCHU9i0W4i1W4SIlFvA+2RcQEMIhC/4tNrItFsClNvIlMJAiJRCQEi1W8iRQk6GNv///HRbQA +AAAAx0XEAAAAAIN91AEPhOsAAACLXdSF23VKi0XEi020AciFwH4+g0XIAYNVzAD2RRQQdDCLTcwx +0otF0DHRi1XIMdAJwXUdg33ACH0X/0XA9kUUCHR/x0XIAQAAAMdFzAAAAACLXbzrDZCQkJCQkJCQ +kJCQkJCLTcy4CgAAAItVyIlEJAgxwIlMJASJRCQMiRQk6J8uAAD/TbwEMItNvIgBuAoAAACLVcyJ +RCQIMcCJRCQMi0XIiVQkBIkEJOhELQAAiVXMi1XMiUXIC1XIdabp4/v///9NvItVvMYCMItNrItF +sIlMJAgpyolVvIlEJASJFCToZW7//+lY////i0W0mYnBi0XIidMx0oPgAQHBEdOD+wB3BYP5AHYS +i0XEQIP4BQ+P/f7//+k2////g33EBevvg0XIAcdFxAAAAACDVcwA6Wv+//+LhXT///+LVbSD4AEB +0IP4Ag+POv7//+lP/v//ifaLVbSNBBo5RdAZwPfQg8AD6dH9//+J+QnxD4QR+v//iXQkCItF2ItV +3Il8JAyJBCSJVCQE6JctAACJ0QnBD4Xt+f//iXQkCItF2ItV3Il8JAyJBCSJVCQE6EMsAACJVZyL +TeSJVCQMi1XgiUwkBIlFmIlEJAiJFCToUy0AAInDidbB4wIPpMYCAcOLRZgR1otVnA+k3gEB24lE +JAiJVCQMiRwkiXQkBOgkLQAAidGJwgHSiVWQi1WcD6TBAYtFmIlNlItN5IlUJAyLVeCJTCQEiUQk +CIkUJOjEKwAAiUXIi0WYiVXMi1WciUQkCIkcJIlUJAyJdCQE6KQrAACJRcSLVZw5VZR3KnIIi0WY +OUWQcyCDfZQAx0W0AQAAAA+HHPz//4N9kAAPhxL8///pBvz//4tVlDlVnHcWcgiLRZA5RZhzDMdF +tAMAAADp7/v//8dFtAIAAADp4/v//5CNdCYAVYnlg+wIxwQkqlwFCOhGav//Mcm6AAIAAIXAdQe6 +AAQAADHJieyJ0InKXcONdCYAVYnlg+woiV30i10IiXX4MfaF24l9/It9DA+E7AAAAIA7Jw+E2AAA +AIkcJLgEAAAAuaiKBQiJRCQMuLSKBQiJRCQIiUwkBOhV7v//hcB4MccHAQAAAIsUhbSKBQjHRwQA +AAAACdaLRRCJMDHAi130i3X4i338iexdw420JgAAAACJfCQMuPCKBQiJRCQQMcCJRCQIjUXwiUQk +BIkcJOiuGwAAhcB1xotV8IA6AHVCD7YDLDA8CXauifaNvCcAAAAAOdN0DEMPtgMsMDwJd/LrlYPO +QIB7/0J0BYPOIOuHgc6AAAAAgHv+aQ+Fd////+vpuAIAAADpcv///422AAAAAL4EAAAAQ+kd//// +xwQkl1wFCOgRaf//hcCJww+F/v7//+iq/v//iQeJVwTpNf///1WJ5YPsKA+2RQyJXfSLXRCJdfiL +dQiIRe+NRfCJffyJRCQIiVwkBIk0JOih/v//i1MEicGLA4nXCccPhOUAAACFyXQsgH3vAHQmg/kB +D4ShAAAAg/kBD4LdAAAAg/kCdGKD+QN0Go12AI28JwAAAACLRfCLXfSLdfiLffyJ7F3DxwQkAAAA +ALgFAAAAiUQkCLgCiwUIiUQkBOhqaP//iUQkDLgNiwUIiXQkEIlEJAjHBCQCAAAAMcCJRCQE6Ids +///rrccEJAAAAAC5AosFCLsFAAAAiVwkCIlMJAToJ2j//4lEJAy6QIsFCIl0JBCJVCQI67vHBCQA +AAAAuAUAAAC/AosFCIlEJAiJfCQE6PZn//+JdCQQvh+LBQiJRCQMiXQkCOuK6Hb9//+JA7kBAAAA +iVME6Qf////oqmj//5CQVYnlV1ZTg+wMix2oqAUIi3UIhdt0FY12AI28JwAAAAA5M3ROi1sIhdt1 +9Yk0JOjlZ///xwQkDAAAAInH6I8UAACJMDHSicOF/3QMiweJBCToexUAAInCiVMEoaioBQiJQwiJ +0IkdqKgFCIPEDFteX13Di0ME6/OJ9o28JwAAAABVieVXVlOD7AyLHaioBQiLfQiF23QgD7YHiEXz +jXQmAA+2VfOLQwQ4EA+EpQAAAItbCIXbdeqLHayoBQiF23QiD7YHiEXzjXYAjbwnAAAAAA+2VfOL +QwQ4EHRli1sIhdt17ok8JOj+af//xwQkDAAAAInG6NgTAACJPCSJw+jOFAAAiUMEhfZ0H4tGCIna +iQOhqKgFCIlDCIkdqKgFCIPEDInQW15fXcOhrKgFCDHSiUMIiR2sqAUI6+SNtgAAAACJfCQEiQQk +6Exm//8x0oXAdYnryol8JASJBCToOGb//4XAidoPhUX////rso10JgBVieVXVlOD7AyLHbCoBQiL +dQiF23QVjXYAjbwnAAAAADkzdE6LWwiF23X1iTQk6JVm///HBCQMAAAAicfoHxMAAIkwMdKJw4X/ +dAyLB4kEJOgLFAAAicKJUwShsKgFCIlDCInQiR2wqAUIg8QMW15fXcOLQwTr84n2jbwnAAAAAFWJ +5VdWU4PsDIsdsKgFCIt9CIXbdCAPtgeIRfONdCYAD7ZV84tDBDgQD4SlAAAAi1sIhdt16osdtKgF +CIXbdCIPtgeIRfONdgCNvCcAAAAAD7ZV84tDBDgQdGWLWwiF23XuiTwk6H5k///HBCQMAAAAicbo +aBIAAIk8JInD6F4TAACJQwSF9nQfi0YIidqJA6GwqAUIiUMIiR2wqAUIg8QMidBbXl9dw6G0qAUI +MdKJQwiJHbSoBQjr5I22AAAAAIl8JASJBCTo3GT//zHShcB1ievKiXwkBIkEJOjIZP//hcCJ2g+F +Rf///+uykJCQkFWJ5VdWU4PsHIt1DItFEItdCIX2xkAVAI14FXhTjXYAiRwkMcm4CgAAAIlMJAxP +iXQkBIlEJAjodCMAAAQwugoAAACIBzHAiRwkiXQkBIlUJAiJRCQM6MUhAACJ0YnDCcGJ1nW7g8Qc +ifhbXl9dw5CJHCS4CgAAAE+JRCQIMcCJdCQEiUQkDOgkIwAAsTAowbgKAAAAiA+JRCQIMcCJHCSJ +dCQEiUQkDOhzIQAAicOJ1onQCdh1uU/GBy3rqJCQkFWJ5YPsGIld/ItdCIkcJOhEZf//iUQkBItV +DIkcJIlUJAjoCQAAAItd/InsXcOJ9lWJ5Vcx/1ZTg+wsi3UIi0UMAfCJReDo3Wb//4P4AQ+GJwEA +ADt14HNHjbQmAAAAAA++BoP4Xw+PBAEAAIP4QX0hg/ggfDqD+CN+F4PoJYP4GusNkJCQkJCQkJCQ +kJCQkHceRkc7deByx420JgAAAACJ+IPELFteX13DjbYAAAAAx0XoAAAAAMdF7AAAAACJ9ol0JASN +ReiJRCQMi0XgKfCJRCQIjUXkiQQk6Nlk//+D+P+Jw3Ryg/j+dFaFwHUFuwEAAACLReSJBCToOWf/ +/4XAeB0Bx41F6AHeiQQk6OZm//+FwHSq6Xn///+QjXQmAPZFEAK4/////w+Fc////4tF5IkEJOie +ZP//hcB1x0frxPZFEAG4/////w+FUv///4t14Ok7////9kUQAbj/////D4Qr////6Tb///+NdCYA +g+hhg/gd6RX///87deAPgxz////o72b//4nBkI10JgAPtgZGD7bQiwEPtxRQ9sZAdA5HO3Xgcujp +8/7//412APZFEAK4/////w+F4/7//9Hqg/IBg+IBAdfr2JCQkJCQVYnlV1ZTg+wci10IhdsPhJwA +AACLRQyJBCToger//4lF8IkcJOheY///i1XwicaNRAICiQQk6M1i//+JxzHAhf90WIlcJASJdCQI +iTwk6MVl//+F9onDdBeAeP8vdESLVQyAOi90CcYAL0OQjXQmAItFEIXAdAWLRRCJGIkcJItV8ItF +DIlUJAiJRCQE6PZj//+LVfDGBBoAifiDxBxbXl9dw4tFDIA4L3XFS+vCifaLRQyJBCToDWT//4tV +EInHhdJ01YtVEIkC686QjXQmAFWJ5YPsGItFEIlEJAiLRQyJRCQEi0UIiQQk6BH///+FwHQEiexd +w+j0DQAAkJCQkFWJ5YPsGItFDIlEJAi4BQAAAIlEJASLRQiJBCToTwoAAInsXcONdCYAjbwnAAAA +AFWJ5YPsCMcEJAAAAACLRQiJRCQE6Lf///+J7F3DkJCQVYnlg+wYiV34iXX86Fdh//+LMInDxwQk +JAAAAOjvDQAAi1UIicGF0nRGiwKJAYtCBIlBBItCCIlBCItCDIlBDItCEIlBEItCFIlBFItCGIlB +GItCHIlBHItCIIlBIInIiTOLXfiLdfyJ7F3DjXQmALrAqQUI67OJ9o28JwAAAABVieWLRQiFwHQG +XYsAw4n2uMCpBQjr84n2jbwnAAAAAFWJ5YtVCIXSdAeLRQyJAl3DusCpBQjr8pCNtCYAAAAAVYnl +g+wID7ZNDIkcJItFCIl0JASLXRCIysDqBQ+20sHiAoXAjXQCBHQmixaD4R+D4wGJ0NP4g+ABMcPT +4zHaiRaLHCSLdCQEiexdw410JgCNssSpBQjr0pCNtCYAAAAAVbgFAAAAieWD7BiJXfyLXQiJRCQI +xwQkAAAAAIlcJATow1///znYdAeLXfyJ7F3Dg30MBnXzuIKLBQjr7I12AFWJ5VdWU4PsfMdF1AAA +AADHRdAAAAAAx0XMAAAAAMdFyAAAAADofmL//0gPlMCDfRgGD7bAiUXEdzSLRRj/JIXciwUIi1UM +OVXUcwaLTQjGASLHRdQBAAAAx0XIAQAAAMdF0IKLBQjHRcwBAAAAx0XYAAAAAI10JgCNvCcAAAAA +g30U/w+EBgUAAItVFDlV2A+EqwQAAItdyIXbdEOLTcyFyXQ8i0XYi1XMAdA7RRR3L/yLXcyLdRCL +RdiLfdCJ2QHGOdvzpnUYi3UMOXXUcwqLRQiLVdTGBAJc/0XUjXYAi00Qi13YD7YMCw+2wYP4fohN +qA+HOwIAAP8khfiLBQiLRciLddiFwI1WAQ+EnwAAAItFDDlF1HMKi1UIi03UxgQRXP9F1ItdDDld +1HMKi3UIi0XUxgQwMP9F1ItVDDlV1HMKi00Ii13UxgQLMP9F1MZFqDCNtgAAAACNvwAAAACLXciL +TdiF241RAXRDD7ZFqItdHA+2TajA6AUPtsCLRIMEg+Ef0/ioAXQli0UMOUXUcwqLVQiLTdTGBBFc +/0XUi13YjVMBjXYAjbwnAAAAAIt1DDl11HMND7ZFqItNCItd1IgEC/9F1IlV2Omw/v//sGGNtCYA +AAAAjbwnAAAAAIt9yItN2IX/jVEBdMOIRajrmbBi6+qwdIN9GAF14otVHItNFItdEIlUJBSLdQy6 +AgAAAIlUJBCLRQiJTCQMiVwkCIl0JASJBCTo0v3//4PEfFteX13DsG7rvrB266KwZuuesHLrsoN9 +GAF0soN9GAIPhQr///+LXQw5XdRzCot1CItF1MYEMCf/RdSLVQw5VdRzCotNCItd1MYEC1z/RdSL +dQw5ddRzCotFCItV1MYEAif/RdTpxv7//4N9GAEPhFr///+DfRgDD4Wy/v//i1XYg8ICO1UUD4Oj +/v//i3UQi0XYgHwwAT8PhZL+//8PtkwwAg++wYPoIYP4HQ+Hfv7///8khfSNBQiJVdiLVQw5VdSI +TahzCotNCItd1MYECz//RdSLdQw5ddRzCotFCItV1MYEAlz/RdSLTQw5TdQPg23///+LXQiLddTG +BB4/6V7///+LXcSF2w+E9wAAAMdFpAEAAADorWD//w+2VaiLAA+3FFCB4gBAAACJVaCLRciLXdiF +wI1TAQ+EQf7//4tFoIXAD4Xm/f//i03Yi0WkAcGNtCYAAAAAjbwnAAAAAIt9yIX/dGmLdaCF9nVi +i3UMOXXUcwqLRQiLVdTGBAJc/0XUi10MOV3UcxIPtkWoi3UIi1XUwOgGBDCIBDL/RdSLXQw5XdRz +FA+2RaiLdQiLVdTA6AMkBwQwiAQygGWoB/9F1IBFqDCJ9o28JwAAAACLVdhCOdEPhqT9//+LXQw5 +XdRzDQ+2RaiLXQiLddSIBB7/RdSLRRCJVdgPtgQCiEWo6Vr///+DfRT/x0XgAAAAAMdF5AAAAADH +RaQAAAAAx0WgAQAAAA+ErQAAAI10JgCNXeCLTaSLRRSJXCQMi3UQi13YAcsp2AHeiUQkCI1F3Il0 +JASJBCTorFz//4XAicd0M4P4/3Rpg/j+dDiLRdyJBCToUV7//4XAjU3gD5TAAX2kiQwkD7bASCFF +oOi3Xv//hcB0m4N9pAEPho/+///ppv7//ztdFMdFoAAAAABz5YA+AHTg/0Wki0XYi1WkAdA7RRRz +0ItVEIA8EAB15+vFx0WgAAAAAOu8i00QiQwk6MRb//+JRRTpRP///w+2Rajp0/z//4N9GAEPhTH8 +///pyvz//4t12IX2D4Uh/P//6+SLTdCFyXQri03QD7YBhMB0IYtdDDld1HMJi3UIi1XUiAQy/0XQ +/0XUi03QD7YBhMB134tdDDld1HMKi3UIi0XUxgQwAItF1Omh/P//jXYAi3UQi0XYgDwwAOnx+v// +i1UMOVXUcwaLTQjGASfHRdQBAAAAx0XQLYsFCOmn+v//x0XIAQAAAOmi+v//xwQkhIsFCItdGIlc +JATo4fn//4lcJASJx8cEJC2LBQjoz/n//4l90InCgD8AdCWLdQw5ddRzD4tN0ItdCIt11A+2AYgE +Hv9F0P9F1ItF0IA4AHXbx0XIAQAAAIlV0IkUJOimWv//iUXM6TT6//+NtgAAAABVieWD7CiJffyL +fRiJXfSJdfiF/3RC6K1Z//+LMInDiXwkFIsHiUQkEItFFIlEJAyLRRCJRCQIi0UMiUQkBItFCIkE +JOh3+f//iTOLXfSLdfiLffyJ7F3Dv8CpBQjrt5BVieVXVlOD7BzoWln//4t9CIsAhf+JRfAPiDAB +AACLRQg5BTSjBQh3YonDQ4003QAAAACJ8MHoAznDD4UTAQAAiw1AowUIgfk4owUID4TUAAAAiXQk +BDH2iQwk6NAFAACjQKMFCIsVNKMFCIl0JASNDNCJ2IkMJCnQweADiUQkCOgjXP//iR00owUIoUCj +BQiLVQiLPNCLdNAEi0UUi1UQiXwkBIlEJBCLRQyJVCQMiTQkiUQkCOjl/v//Ocd3U414AYsdQKMF +CDHAgf7AqAUIi1UID5TAiTzTiXwkBEgh8IkEJOhIBQAAicaLVRSLRQiJdMMEi0UQiVQkEItVDIlE +JAyJfCQEiVQkCIk0JOiO/v//6FFY//+LVfCJEIPEHInwW15fXcPHBCQIAAAA6N4EAACjQKMFCInB +ixU8owUIoTijBQiJUQSJAekE////6JVY///oSAQAAJCNtCYAAAAAVbjAqQUIieWD7BiJRCQMuP// +//+JRCQIi0UMiUQkBItFCIkEJOh2/v//iexdw4n2VYnlg+wIxwQkAAAAAItFCIlEJATot////4ns +XcONdgBVuQgAAACJ5fyD7DiLVQyJffyNfcyJVcgx0onQiV34i10I86uLVciJ2IkTi1XMiVMEi1XQ +iVMIi1XUiVMMi1XYiVMQi1XciVMUi1XgiVMYi1XkiVMci1XoiVMgi134i338iexdwgQAifaNvCcA +AAAAVYnlg+xIiV38i0UMjV3IiRwkiUQkBOh1////g+wEuP////+JRCQIi0UQiVwkDIlEJASLRQiJ +BCToo/3//4td/InsXcONtgAAAACNvwAAAABVieWD7EiJXfyLRQyNXciJHCSJRCQE6CX///+LRRSD +7ASJXCQMiUQkCItFEIlEJASLRQiJBCToVf3//4td/InsXcONtCYAAAAAjbwnAAAAAFWJ5YPsGMcE +JAAAAACLRQyJRCQIi0UIiUQkBOhA////iexdw422AAAAAI2/AAAAAFWJ5YPsSA++VQyJXfyhwKkF +CI1dyIkcJIlFyKHEqQUIiVQkBIlFzKHIqQUIiUXQocypBQiJRdSh0KkFCIlF2KHUqQUIiUXcodip +BQiJReCh3KkFCIlF5KHgqQUIiUXouAEAAACJRCQI6GP1//+JXCQMuP////+JRCQIi0UIxwQkAAAA +AIlEJATog/z//4td/InsXcONtgAAAACNvwAAAABVuDoAAACJ5YPsCIlEJASLRQiJBCToRv///4ns +XcOQkFWJ5VdWU4PsDIt1EIt9DIX2eDqNtgAAAACNvCcAAAAAiXQkCItFCIl8JASJBCToxVT//4XA +icN5Cuh6Vf//gzgEdN2DxAyJ2FteX13DvgDg/3/rzJCQkJCQkJCQkJCQkFWJ5VdWU4PsHItFEItd +CIt1DI14FcZAFQCNtCYAAAAAiRwkMcm4CgAAAIlMJAxPiXQkBIlEJAjoxBYAAAQwugoAAACIBzHA +iRwkiXQkBIlUJAiJRCQM6HUVAACJw4nWidAJ2HW7g8QcifhbXl9dw5BVieVWU4PsIItVDItdCItN +EIXSi0UUi3UYD4TUAAAAiUQkELhYjwUIiUwkDIlUJAiJRCQEiRwk6CxU///HBCQAAAAAuAUAAACJ +RCQIuGSPBQiJRCQE6C5U//+JRCQEiXQkCIkcJOj+U///i0MUO0MYc27GAAr/QxSJXCQEoUSjBQiJ +BCTov1P//4tDFDtDGHM1xgAK/0MUiV0MugUAAAC4wI4FCIlUJAiJRCQExwQkAAAAAOjQU///iUUI +g8QgW15d6YJT//+JHCS5CgAAAIlMJAToIVP//+u+jbQmAAAAAIkcJL4KAAAAiXQkBOgHU///64WJ +RCQMuHSPBQiJTCQIiUQkBIkcJOhcU///6Sv///+QkJCQkJCQkJCQkJCQkJBVieWD7Bih5KkFCIXA +dVHHBCQAAAAAuXuPBQi4BQAAAIlMJASJRCQI6DtT//+JRCQMuk6HBQgxwIlEJAShMKMFCIlUJAiJ +BCToW1f//8cEJAEAAADoP1b//420JgAAAAD/0OurjbYAAAAAjb8AAAAAVYnlg+wIi0UIiQQk6JdT +//+FwHQEiexdw+hy////ifZVieWD7AiLVQiLRQyJFCSJRCQE6MBU//+FwHQEiexdw+hL////jXQm +AI28JwAAAABVieWD7AiLRQyJRCQEi0UIiQQk6NBV//+FwHQEiexdw+gb////kJCQkJCQkJCQkJBV +ieVXVlOD7Ay7gAAAAIt1COsNkJCQkJCQkJCQkJCQkIkcJOhY////iUQkBInHiVwkCIk0JOg+Uf// +hcB4JTnYchOJPCTojlX//40EG4XAicN5zeslxgQ4AIn4g8QMW15fXcPocFL//4swicOJPCToZFX/ +/4kzMcDr4eiR/v//kFWJ5YPsGIld/ItdCIkcJOgUU///QIkEJOjj/v//iQQkiVwkBOg/Vv//i138 +iexdw1WJ5YPsEIl1+ItNDIt1CIld9Il9/Is+ifsPr9mJ2Jn3+bkBAAAAOceJRfB0E4td9InIi3X4 +i338iexdw410JgCJHjHJ6+eNdgCNvCcAAAAAVYnlV1ZTg+wMi10Qi30Ii3UMS4P7/3QjkI20JgAA +AACJdCQEiTwk6IT///+FwLoBAAAAdQhLg/v/deUx0oPEDInQW15fXcOJ9o28JwAAAABVieVXVlOD +7ByLdRCD/iQPhycCAACLfQyF/w+EFAIAAOhjUf//xwAAAAAAicMxwIlEJAyLRQiJdCQIiXwkBIkE +JOhSUv//iUXsiwOFwA+F1wEAAIsfO10ID4SSAQAAi10YhdsPhLcAAACLHw+2A4TAD4SqAAAAx0Xo +AQAAAA++wL4ABAAAiUQkBItFGIkEJOj0T///hcAPhAIBAACLRRi5MAAAAIlMJASJBCTo2E///4XA +dCwPvkMBg/hED4T9AAAAg/hED48FAQAAg/hCD4TrAAAAjbYAAAAAjbwnAAAAAA++A4PoQoP4NQ+H +rwAAAP8khdiPBQi6AAIAAIlUJASNReyJBCToVv7//422AAAAAInChdK4AwAAAHUPi0XoAQeLReyL +VRSJAjHAg8QcW15fXcO4AAQAAIlEJATrwrgGAAAAjbQmAAAAAI28JwAAAACJRCQIiXQkBI1F7IkE +JOhN/v//66u4BQAAAOvkuggAAACJVCQI6924BwAAAOvSuAMAAADry7gBAAAA68S4AgAAAOu9uwQA +AACJXCQI67aLReyLVRSJArgCAAAA6Xr///+5AgAAAIlMJATpQf///8dF6AIAAAC+6AMAAOkR//// +g/hpD4UI////gHsCQg+F/v7//8dF6AMAAADp8v7//4n2i3UYhfZ0KQ+2A4TAdCKLVRgPvsCJRCQE +iRQk6HhO//+FwHQMx0XsAQAAAOk+/v//uAEAAADpAP///7gDAAAA6fb+//+NffDp5P3//8cEJKCP +BQi4xo8FCIlEJAy4dgAAAIlEJAi4zo8FCIlEJATo1U7//5CQkJCQkJCQkJCQkJBVieVXVlOD7ByL +RQyLTQiZiUQkCItEJAiJVCQMixmLfCQMi3EE9+MPr/uJReiJ0AH4i3wkCItV6A+v/okUJAH4iUXs +i03siUwkBOhBDwAAifEx2DHRCcG6AQAAAHUQi1Xsi00Ii0XoiVEEMdKJAYPEHInQW15fXcOQjbQm +AAAAAFWJ5VdWU4PsDItdEIt9CIt1DEuD+/90I5CNtCYAAAAAiXQkBIk8JOhU////hcC6AQAAAHUI +S4P7/3XlMdKDxAyJ0FteX13DifaNvCcAAAAAVYnlV1ZTg+wsi3UIg30QJA+HQAIAAIt9DIX/D4Qt +AgAA6BJS//+LCInzjbYAAAAAD7YTD7bC9kRBASB0A0Pr8ID6LbgBAAAAD4T/AAAA6PVN///HAAAA +AACJwzHAiUQkDItFEIl8JASJNCSJRCQI6NRO//+JReCLA4lV5IXAD4XAAQAAix858w+EdQEAAItd +GIXbD4SkAAAAix8PtgOEwA+ElwAAAMdF3AEAAAAPvsC+AAQAAIlEJASLRRiJBCTohEz//4XAD4Tn +AAAAi0UYuTAAAACJTCQEiQQk6GhM//+FwHQfD75DAYP4RA+E4gAAAIP4RA+P6gAAAIP4Qg+E0AAA +AA++A4PoQoP4NQ+HoQAAAP8khfSQBQi6AAIAAIlUJASNReCJBCToA/7//4nChdK4AwAAAHUVi0Xc +AQeLReCLVeSLTRSJATHAiVEEg8QsW15fXcO4AAQAAIlEJATrwrgGAAAAiUQkCIl0JASNReCJBCTo +OP7//+uzuAUAAADr5LoIAAAAiVQkCOvduAcAAADr0rgDAAAA68u4AQAAAOvEuAIAAADrvbsEAAAA +iVwkCOu2i0Xgi00Ui1XkiQG4AgAAAOuFuQIAAACJTCQE6U/////HRdwCAAAAvugDAADpH////4P4 +aQ+FFv///4B7AkIPhQz////HRdwDAAAA6QD///+LdRiF9nQwD7YDhMB0KYtNGA++wIlEJASJDCTo +JUv//4XAdBPHReABAAAAx0XkAAAAAOlU/v//uAEAAADpCf///7gDAAAA6f/+//+Nfezpy/3//8cE +JKCPBQi45pAFCIlEJAy4dgAAAIlEJAi4zo8FCIlEJAToe0v//5CQkFWJ5YPsGIld9ItFCItdDIl1 ++Il9/IkEJOj7Sv//icYxwIX2dAz8uQsAAACJ3/OlidiLXfSLdfiLffyJ7F3DifZVieVXVlOD7BSL +RSCFwA+EPgEAAItQFItYHIlV8ItQCIld7ItYBIsAiVXoiV3kiUXgi0UIwfgC9kUIA42w2wEAAHUG +jbDaAQAAi0XwwfgCjbjbAQAA9kXwA3UGjbjaAQAAux+F61GJ8Inx9+uJ8MH4Hyn+wfoDKcKNBJKN +BIApwcHpH4n4KcqJ0ffrifiJ+8H4H4t9DMH6AynCjQSSjQSAKcOJ2MHoHynCicuJ0MH7AinBi0Xw +wfoCKc4p04tVCI0cM4t17ItN5CnCjQTSjQTCjQSAi1UQAfgp8AHYi13ojQRAjQTCKdiJwsHiBCnC +i0UUjRSQKcqLRRiJ0cHhBCnRjQyIi0XgKcGLRRyLGDHAjRQZOdoPncDB6R85yHUWhdt4HjHAgfv/ +//9/D5XABf7//3+JwoPEFInQW15fXcOJ9jHAgfsAAACAD5TALQAAAIDr4ItVHIsa68WNtCYAAAAA +VYnlV1ZTg+xci1UMi0UQiRQkiUQkBP9VCIXAicJ0CoPEXInQW15fXcOLTQyLAYXAdO2JwzH/jXYA +jbwnAAAAAIXbjUf/eAONRwE5w3RShdt4Q4nYKfjR+I0EOIlFtIt1DItVEItFtIkGiVQkBIk0JP9V +CIXAicJ0F/y5CwAAAI19uInG86WLfbTrtZCNdCYAi12066uJ+CnY0fiNBBjru4XSD4V3////hf8P +hG/////8i0UMuQsAAACLVRCJOI1FuIt9EInG86XpUv///412AFWJ5Ve/BgAAAFYx9lOB7NwAAACL +RQiJvXj///+LVQiLTQiLAItSBItJCImFdP///4tFCImVcP///4tVCItADImNbP///4tKEIt6FImF +aP///4tCIImFZP///7irqqoq9+mJyMH4H9H6KcKNBFLB4AIpwYnLwesfKdqNFDr2wgOJlVz///91 +NrgfhetR9+qLhVz////B+gXB+B8pwo0Eko0EgMHgAjmFXP///w+FewQAAInQg+ADSA+EbwQAAI0U +NgHyjQQbjRSWAdiNBIIByIuVaP///4tNEA+3hADgkQUIiwmNRAL/iYVY////i4V0////iY1U//// +iYVQ////i5VQ////wegfSCHQg/g7iYV0////fgu+OwAAAIm1dP///4u1VP///4uFXP////fewfgC +jZjbAQAA9oVc////A3UGjZjaAQAAwb1o////ConYuh+F61H36onZidjB+B/R/8H6AynCjQSSjQSA +KcGJyMHoHynCKdOJ0YuVXP///8H5Ao2MCyP+//+LnVj///+D6kaNBNKNBMKNBICLlWz///8B2AHI +jQRAjQTCi41w////icLB4gQpwo0UkYnQi41o////weAEKdCLlXT///+NBIKLlWz///8p8ImFfP// +/41EOd3B+g4B0IuVcP///8H6FAHQi5V8////wfoaKcIPiCMDAAC7FQAAADnTfTiLjXz////30YnK +wfoaKcIPiP4CAAA5077/////D4zQAAAAi5V8////iciJjXz///8p0AGFVP///4u1fP///zHAiYVg +////ifeJdYSNdgCNvCcAAAAAi00MjVWEjUW4iVQkBIkMJIlEJAjot/z//4lEJBiLlXD///+NRYSJ +RCQUi41s////i4V0////iVQkDIuVXP///4lEJBCLhVj///+JTCQIiRQkiUQkBOgW+///i12EicE5 +w4mdTP///w+EMwEAADn7dD//jXj///90H4lNhDHAifeDfdgAi7VM////D5XAiYVg////6Wj///++ +/////4HE3AAAAInwW15fXcONtgAAAAA583S9i1XYhdJ4JIuFZP///4XAD4i6AAAAi71k////hf8P +lcCF0g+VwjHQqAF0kouFVP///4u1fP///4tVEAHYKfCJAotFuDmFUP///3Roi410////MdKFyXUF +g/g8dGyLjVD///+LhXT///+NPBkpwjnfjTQ6D5zAwekfD7bYMcsxwDn+D5zAvv/////B6h8x0AnD +jVWEiRQkjUW4iUQkBP9VDIXAD5TAD7bACcMPhTn///+LXYT8uQsAAACLfQiNdbjzpYne6SH///+6 +AQAAAOuNMcCF0g+VwDmFYP///w+P2/7//+lE////kI10JgCLRdg5hWT///8PhDD///+LlWT///+F +0g+IIv///4XAD4ga////uHAsCQC/kNP2/4mFSP///4m9RP///4u1RP///7//////jQQeMdI52A+d +wolFgIn4wegfOcJ1NIuNSP///4PHAoP/AY00Tn7Zga1E////cCwJAIHBcCwJAIH5KP4FEImNSP// +/3yw6a/+//+LRQyNTYiNXYCJTCQIiVwkBIkEJOiH+v//i5Vk////OVWodAWLXYTro4lcJBSLlXT/ +//+NRYiJRCQYi41w////i4Vs////iVQkEIuVWP///4lMJAyLjVz///+JVCQEiUQkCIkMJOjW+P// +iUWEi00MjVWEiVQkBI1FuIlEJAiJDCToGvr//4tdhOkh/v//99rp+/z///fa6db8//++AQAAAOmH ++///jXYAjbwnAAAAAFW46KkFCInlg+wYiUQkCLhwSQUIiUQkBItFCIkEJOid+v//iexdw5CQkJCQ +kJCQkFWJ5YPsGOitAAAAgcF1UwAA220I2YFE7f//iXX4iX383eHf4J52Gd3Y3dgxwDHSi3X4i338 +iexdw420JgAAAADZffbZwTH2D7dF9tiJSO3//2YNAAxmiUX02W30333o2W32i0XoicdQVt8sJIPE +CIXAeD3e6t3p3+CedhrZ4DHS2W30333o2W32i0XoKcYZ14nwifrrmdlt9N996Nlt9jHSi0XoAcYR +1+vmjbYAAAAA2IEQ5v//67uLDCTDkJCQkFWJ5VdWg+woi30Mx0XoAAAAAIt1CItFEMdF7AAAAACF +/4tVFMdF9AAAAAAPiD8BAACF0g+IKAEAAIl14IXSif6JRdSJVeR1eDn4dkSJ+otF4Pd11InHjXYA +jbwnAAAAAMdF0AAAAACJfeiLVdCLTfSLReiJVeyFyYtV7HQH99iD0gD32oPEKF5fXcOQjXQmAIt9 +1IX/dQ64AQAAADHJMdL38YlF1InwMdL3ddSJRdCLReD3ddSJx+uujbQmAAAAADl95HYLMf/rl420 +JgAAAAAPvUXkiceD9x91HTt15HcOi1XUMf85VeAPgnL///+/AQAAAOlo////i1XkuCAAAACJ+Sn4 +iUXc0+KLRdQPtk3c0+iJ+dNl1AnCi0XgD7ZN3IlV5Iny0+qJ+dPmD7ZN3NPoCcaJ+Ynw93Xk02Xg +idaJx4tF1PfnOfJ3ETnyD4UO////O0XgD4YF////T+n//v//91X099iD0gD32unJ/v//x0X0//// +//feg9cA99/prv7//5CQkJCQkJCQkJCQkJBVieVXVoPsMIt9DMdF4AAAAACLdQiLRRDHReQAAAAA +hf+LVRTHRewAAAAAD4i/AQAAhdIPiKsBAACJRcyNTfCF0olN6IlV3Il12Il91HV5Ofh2TYnwifr3 +dcyJVdiLfeiF/3Qbx0XkAAAAAItF2IlF4ItF6ItV4ItN5IkQiUgEi0XshcB0Cvdd8INV9AD3XfSL +RfCLVfSDxDBeX13DjXYAi0XMhcB1DbgBAAAAMdL3dcyJRcyLRdSLVdz3dcyJVdSLRdj3dczrkotV +1DlV3HYUiXXgi0XgiVXki1XkiUXwiVX065sPvUXcicaD9h91UYtV3DlV1HcIi03MOU3YchKLVdSL +RdgrRcwbVdyJRdiJVdSLdeiF9g+EY////4tF2ItV1ItN6IlF4ItF4IlV5ItV5IkBiVEE6UT///+Q +jXQmAItV3LggAAAAifEp8It91IlF0NPii0XMD7ZN0NPoifHTZcwJwotF2A+2TdCJVdyLVdTT6onx +0+cPtk3Q0+gJx4l91In4ifH3ddyJVdTTZdj3Zcw7VdSJx3dDO1XUdDmLTeiFyQ+E1v7//4tN1ItF +2Cn4GdGJTdSJyg+2TdCJRdjT4onx0+gJwotF1IlV4NPoiUXk6Zr+//87Rdh2wit9zBtV3Ou699iD +0gD32ulJ/v//x0Xs//////feg9cA99/pLv7//5CQkJCQkJCQkJCQkJBVieVXVoPsIItVFMdF8AAA +AACLdQiLfQzHRfQAAAAAi0UQhdKJdeiJ/olF3IlV7HVfOfh2K4n6i0Xo93XciceQx0XYAAAAAIl9 +8ItV2ItF8IlV9ItV9IPEIF5fXcONdgCLRdyFwHUOuAEAAAAxyTHS9/GJRdyJ8DHS93XciUXYi0Xo +93Xcicfrvo20JgAAAAA5fex2CzH/66eNtCYAAAAAD71F7InHg/cfdRk7dex3CotV3DH/OVXocoa/ +AQAAAOl8////i1XsuCAAAACJ+Sn4iUXk0+KLRdwPtk3k0+iJ+dNl3AnCi0XoD7ZN5IlV7Iny0+qJ ++dPmD7ZN5NPoCcaJ+Ynw93Xs02XoidaJx4tF3PfnOfJ3ETnyD4Ui////O0XoD4YZ////T+kT//// +kJCQVYnljU3wV1aD7DCLVRTHReAAAAAAi3UIi30Mx0XkAAAAAItFEIXSiU3siVXciUXMiXXYiX3U +dWg5+HY5ifCJ+vd1zIlV2ItN7IXJdBvHReQAAAAAi0XYiUXgi0Xsi1Xgi03kiRCJSASLRfCLVfSD +xDBeX13Di3XMhfZ1DbgBAAAAMdL3dcyJRcyLRdSLVdz3dcyJVdSLRdj3dczrpo12AItV1DlV3HYY +iXXgi0XgiVXki1XkiUXwiVX066yNdCYAD71F3InGg/YfdUyLVdw5VdR3CItNzDlN2HISi1XUi0XY +K0XMG1XciUXYiVXUi1XshdIPhHD///+LRdiLVdSLTeyJReCLReCJVeSLVeSJAYlRBOlR////i1Xc +uCAAAACJ8Snwi33UiUXQ0+KLRcwPtk3Q0+iJ8dNlzAnCi0XYD7ZN0IlV3ItV1NPqifHT5w+2TdDT +6AnHiX3UifiJ8fd13IlV1NNl2PdlzDtV1InHd0M7VdR0OYtF7IXAD4To/v//i03Ui0XYKfgZ0YlN +1InKD7ZN0IlF2NPiifHT6AnCi0XUiVXg0+iJReTprP7//ztF2HbCK33MG1Xc67qQkJCQkFWJ5VdW +MfZTg+wM6KAAAACBwyBMAADo9Tr//42TeP3//42DeP3//ynCwfoCOdZzHInXjbQmAAAAAI28JwAA +AAD/lLN4/f//Rjn+cvSDxAxbXl9dw422AAAAAI2/AAAAAFWJ5YPsCIkcJOhCAAAAgcPCSwAAiXQk +BI2DeP3//42TeP3//ynQwfgChcCNcP91EOibAAAAixwki3QkBInsXcP/lLN4/f//ifBOhcB18uvg +ixwkw5CQkJCQkJCQkJCQkFUx0onlg+wYiV386OD///+Bw2BLAACLg2wBAACFwHQCixCJVCQIi0UI +x0QkBAAAAACJBCTofzv//4td/InsXcNVieVTg+wEu7ykBQihvKQFCIP4/3QWjXYAjbwnAAAAAIPr +BP/QiwOD+P919FhbXcNVieVT6AAAAABbgcP3SgAAUujKP///i138ycMAAAAAAAAAAAAAAAAAAAAA +AAAAAAADAAAAAQACAGZ1bGwtaXNvAG5vbmUAY2xhc3NpZnkAZmlsZS10eXBlAGxjAHJjAGVjAG5v +AGZpAGRpAGxuAHBpAGJkAGNkAG1pAGV4AGRvABtbAG0AMDE7MzQAMDE7MzYAMDE7MzUAMDE7MzMA +MDE7MzIAJWIgJWUgICVZACViICVlICVIOiVNAGVzY2FwZQBkaXJlY3RvcnkAZGlyZWQAZnVsbC10 +aW1lAGh1bWFuLXJlYWRhYmxlAGlub2RlAGtpbG9ieXRlcwBudW1lcmljLXVpZC1naWQAbm8tZ3Jv +dXAAaGlkZS1jb250cm9sLWNoYXJzAHJldmVyc2UAd2lkdGgAYWxtb3N0LWFsbABpZ25vcmUtYmFj +a3VwcwBzaQBkZXJlZmVyZW5jZS1jb21tYW5kLWxpbmUAaWdub3JlAGRlcmVmZXJlbmNlAGxpdGVy +YWwAcXVvdGUtbmFtZQByZWN1cnNpdmUAc2hvdy1jb250cm9sLWNoYXJzAHRhYnNpemUAdGltZS1z +dHlsZQBibG9jay1zaXplAGF1dGhvcgBoZWxwAHZlcnNpb24AdmVyYm9zZQBsb25nAGNvbW1hcwBo +b3Jpem9udGFsAGFjcm9zcwB2ZXJ0aWNhbABzaW5nbGUtY29sdW1uAGV4dGVuc2lvbgBhdGltZQBh +Y2Nlc3MAdXNlAGN0aW1lAHN0YXR1cwB5ZXMAZm9yY2UAbmV2ZXIAYXV0bwBpZi10dHkAZGV2X2lu +b19wb3AAbHMuYwAgJWx1AABjb3JldXRpbHMAL3Vzci9zaGFyZS9sb2NhbGUALgBtYWluAC8vRElS +RUQvLwAvL1NVQkRJUkVELy8AZm91bmQAUVVPVElOR19TVFlMRQBMU19CTE9DS19TSVpFAENPTFVN +TlMAUE9TSVhMWV9DT1JSRUNUADUuMC45MQB2ZGlyAC4qfgBpbnZhbGlkIHRhYiBzaXplOiAlcwBp +bnZhbGlkIGxpbmUgd2lkdGg6ICVzAC0tY29sb3IALS1mb3JtYXQALS1pbmRpY2F0b3Itc3R5bGUA +LS1xdW90aW5nLXN0eWxlAC0tc29ydAAtLXRpbWUAKj1AfAB0aW1lIHN0eWxlACVZLSVtLSVkIAAl +WS0lbS0lZCAlSDolTTolUy4lTiAlegAlWS0lbS0lZCAlSDolTQBpbnZhbGlkIHRpbWUgc3R5bGUg +Zm9ybWF0ICVzAFRJTUVfU1RZTEUAcG9zaXgtbG9uZy1pc28AVEFCU0laRQBMU19DT0xPUlMAdGFy +Z2V0AHVucmVjb2duaXplZCBwcmVmaXg6ICVzAHJlYWRpbmcgZGlyZWN0b3J5ICVzADoKAHRvdGFs +ACAgAGNhbm5vdCByZWFkIHN5bWJvbGljIGxpbmsgJXMAJS04cyAAJS04bHUgACVzICUzbHUgACU4 +cyAAIC0+IAAlKnMgACUzbHUsICUzbHUgAApSZXBvcnQgYnVncyB0byA8JXM+LgoAYnVnLWNvcmV1 +dGlsc0BnbnUub3JnAAAAAAAIWgUIpl0FCKtdBQhbXAUIAAAAAAAAAAABAAAAAgAAAAMAAAAAAAAA +EVoFCBZaBQgfWgUIAAAAAAAAAAABAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp +WgUILFoFCC9aBQgyWgUINVoFCDhaBQg7WgUIPloFCKxdBQhBWgUIRFoFCEdaBQiLigUISloFCE1a +BQgAAAAAClsFCAAAAAAAAAAAYQAAAIlaBQgAAAAAAAAAAGIAAACQWgUIAAAAAAAAAABkAAAAmloF +CAAAAAAAAAAARAAAAKBaBQgAAAAAAAAAAIUAAACqWgUIAAAAAAAAAABoAAAAuVoFCAAAAAAAAAAA +aQAAAL9aBQgAAAAAAAAAAGsAAADJWgUIAAAAAAAAAABuAAAA2VoFCAAAAAAAAAAARwAAAOJaBQgA +AAAAAAAAAHEAAAD1WgUIAAAAAAAAAAByAAAACIsFCAAAAAAAAAAAcwAAAP1aBQgBAAAAAAAAAHcA +AAADWwUIAAAAAAAAAABBAAAADlsFCAAAAAAAAAAAQgAAABZaBQgAAAAAAAAAAEYAAAAfWgUIAAAA +AAAAAABwAAAAHVsFCAAAAAAAAAAAiQAAACBbBQgAAAAAAAAAAEgAAAAgagUIAAAAAAAAAACDAAAA +OVsFCAEAAAAAAAAASQAAAAldBQgBAAAAAAAAAIYAAABAWwUIAAAAAAAAAABMAAAATFsFCAAAAAAA +AAAATgAAAFRbBQgAAAAAAAAAAFEAAAAbXQUIAQAAAAAAAACHAAAAX1sFCAAAAAAAAAAAUgAAAABd +BQgBAAAAAAAAAIQAAABpWwUIAAAAAAAAAACIAAAAK10FCAEAAAAAAAAAigAAAHxbBQgBAAAAAAAA +AFQAAAClWgUIAQAAAAAAAACLAAAAhFsFCAEAAAAAAAAAjAAAAPhcBQgCAAAAAAAAAIIAAACPWwUI +AQAAAAAAAACBAAAAmlsFCAAAAAAAAAAAgAAAAKFbBQgAAAAAAAAAAH7///+mWwUIAAAAAAAAAAB9 +////AAAAAAAAAAAAAAAAAAAAAK5bBQi2WwUIu1sFCMJbBQjNWwUI1FsFCN1bBQgAAAAAAAAAAAAA +AAAEAAAAAwAAAAMAAAACAAAAAQAAABFaBQilWgUICIsFCOtbBQimWwUIAAAAAAAAAAADAAAABAAA +AAIAAAAFAAAA9VsFCPtbBQgCXAUIBlwFCAxcBQgAAAAAAgAAAAIAAAACAAAAAQAAAAEAAAAAAAAA +AAAAAAAAAABziwUIE1wFCBdcBQgdXAUIMloFCBFaBQgjXAUIK1wFCChcBQgAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAACAAAAAQAAAAIA +AAANAAAAAwAAAA8AAAAUAAAAcG9zaXgtAAArpgQIgaYECECqBAhAqgQIQKoECECqBAhAqgQIQKoE +CECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQI +QKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhA +qgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECq +BAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoE +CECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQI +QKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhA +qgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECq +BAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoE +CECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECNClBAhAqgQIQKoECECqBAhAqgQI +QKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhA +qgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECq +BAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoE +CECqBAiapgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQI +QKoECECqBAhAqgQItqYECMymBAjppgQI8KYECECqBAj/pgQIDqcECBunBAgrpwQIQKoECECqBAg1 +pwQIQKoECESnBAhAqgQIQKoECFunBAhipwQIcacECIenBAgOqAQIQKoECECqBAgVqAQIQKoECECq +BAhAqgQIQKoECECqBAhAqgQIQKoECECqBAgUpgQIH6gECCmoBAg4qAQIQKoECEeoBAinqAQIuqgE +CO2oBAhAqgQI/KgECCOpBAgqqQQINKkECECpBAhMqQQIVqkECGWpBAh1qQQIhakECJWpBAifqQQI +qakECDCqBAhAqgQIQKoECECqBAhAqgQIQKoECECqBAhAqgQITKoECFiqBAiXqgQIG6sECCurBAh2 +qwQIiqsECM+rBAgUrAQIG6wECCWsBAhqrAQIr6wECJexBAgSsgQIoLIECMKyBAgEswQIM7IECJCy +BAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAiQsgQIkLIE +CJCyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAiQsgQI +kLIECJCyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAiQ +sgQIkLIECJCyBAiQsgQIJLIECCSyBAgksgQIJLIECCSyBAgksgQIJLIECCSyBAiQsgQIkLIECJCy +BAiQsgQIkLIECJCyBAiQsgQIOrIECJCyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAiQsgQIkLIE +CJCyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAiQsgQI +kLIECJWyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAhPsgQIkLIECFayBAhdsgQIkLIECJCyBAhk +sgQIa7IECJCyBAiQsgQIkLIECJCyBAiQsgQIkLIECJCyBAhysgQIkLIECJCyBAiQsgQIebIECJCy +BAiAsgQIkLIECIeyBAiQsgQIlbIECNeyBAjXsgQI17IECNeyBAjXsgQI17IECNeyBAjXsgQI17IE +CNeyBAi3sgQIt7IECLeyBAi3sgQIt7IECLeyBAi3sgQI5rIECOayBAjmsgQI5rIECOayBAjmsgQI +t7IECLeyBAi3sgQIt7IECLeyBAi3sgQIt7IECLeyBAi3sgQIt7IECLeyBAi3sgQIt7IECLeyBAi3 +sgQIt7IECLeyBAi3sgQIt7IECLeyBAi3sgQIt7IECLeyBAi3sgQIt7IECLeyBAj1sgQI9bIECPWy +BAj1sgQI9bIECPWyBAhpzgQIdM8ECJHPBAjQzQQIsM8ECNDPBAhz0AQIDdAECMnQBAjc0AQI79AE +CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXJlZmVyZW5jZS1jb21tYW5kLWxpbmUtc3lt +bGluay10by1kaXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc2l6ZW9mIChzdHJ1Y3QgZGV2X2lu +bykgPD0gX19leHRlbnNpb25fXyAoeyBzdHJ1Y3Qgb2JzdGFjayBjb25zdCAqX19vID0gKCZkZXZf +aW5vX29ic3RhY2spOyAodW5zaWduZWQpIChfX28tPm5leHRfZnJlZSAtIF9fby0+b2JqZWN0X2Jh +c2UpOyB9KQAAAAAAAAAAAAAAAAAAAAAAAGhhc2hfZ2V0X25fZW50cmllcyAoYWN0aXZlX2Rpcl9z +ZXQpID09IDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvL0RJUkVELU9QVElPTlMvLyAtLXF1b3Rp +bmctc3R5bGU9JXMKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYWJjZGZnaGlrbG1ub3BxcnN0 +dXZ3OnhBQkNERkdISTpMTlFSU1Q6VVgxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFJpY2hhcmQgU3Rh +bGxtYW4gYW5kIERhdmlkIE1hY0tlbnppZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABpZ25v +cmluZyBpbnZhbGlkIHRhYiBzaXplIGluIGVudmlyb25tZW50IHZhcmlhYmxlIFRBQlNJWkU6ICVz +AAAAaWdub3JpbmcgaW52YWxpZCB3aWR0aCBpbiBlbnZpcm9ubWVudCB2YXJpYWJsZSBDT0xVTU5T +OiAlcwAAAAAAAGlnbm9yaW5nIGludmFsaWQgdmFsdWUgb2YgZW52aXJvbm1lbnQgdmFyaWFibGUg +UVVPVElOR19TVFlMRTogJXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHVucGFyc2Fi +bGUgdmFsdWUgZm9yIExTX0NPTE9SUyBlbnZpcm9ubWVudCB2YXJpYWJsZQAAAAAAAAAAAAAAAABu +b3QgbGlzdGluZyBhbHJlYWR5LWxpc3RlZCBkaXJlY3Rvcnk6ICVzAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAY2Fubm90IGRldGVybWluZSBkZXZpY2UgYW5kIGlub2RlIG9mICVzAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAFRyeSBgJXMgLS1oZWxwJyBmb3IgbW9yZSBpbmZvcm1hdGlvbi4KAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAABVc2FnZTogJXMgW09QVElPTl0uLi4gW0ZJTEVdLi4uCgAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATGlzdCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgRklM +RXMgKHRoZSBjdXJyZW50IGRpcmVjdG9yeSBieSBkZWZhdWx0KS4KU29ydCBlbnRyaWVzIGFscGhh +YmV0aWNhbGx5IGlmIG5vbmUgb2YgLWNmdHVTVVggbm9yIC0tc29ydC4KCgAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAE1hbmRhdG9yeSBhcmd1bWVudHMgdG8gbG9uZyBvcHRpb25zIGFyZSBt +YW5kYXRvcnkgZm9yIHNob3J0IG9wdGlvbnMgdG9vLgoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAg +LWEsIC0tYWxsICAgICAgICAgICAgICAgICAgZG8gbm90IGhpZGUgZW50cmllcyBzdGFydGluZyB3 +aXRoIC4KICAtQSwgLS1hbG1vc3QtYWxsICAgICAgICAgICBkbyBub3QgbGlzdCBpbXBsaWVkIC4g +YW5kIC4uCiAgICAgIC0tYXV0aG9yICAgICAgICAgICAgICAgcHJpbnQgdGhlIGF1dGhvciBvZiBl +YWNoIGZpbGUKICAtYiwgLS1lc2NhcGUgICAgICAgICAgICAgICBwcmludCBvY3RhbCBlc2NhcGVz +IGZvciBub25ncmFwaGljIGNoYXJhY3RlcnMKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +ACAgICAgIC0tYmxvY2stc2l6ZT1TSVpFICAgICAgdXNlIFNJWkUtYnl0ZSBibG9ja3MKICAtQiwg +LS1pZ25vcmUtYmFja3VwcyAgICAgICBkbyBub3QgbGlzdCBpbXBsaWVkIGVudHJpZXMgZW5kaW5n +IHdpdGggfgogIC1jICAgICAgICAgICAgICAgICAgICAgICAgIHdpdGggLWx0OiBzb3J0IGJ5LCBh +bmQgc2hvdywgY3RpbWUgKHRpbWUgb2YgbGFzdAogICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgbW9kaWZpY2F0aW9uIG9mIGZpbGUgc3RhdHVzIGluZm9ybWF0aW9uKQogICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgd2l0aCAtbDogc2hvdyBjdGltZSBhbmQgc29ydCBieSBuYW1lCiAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdGhlcndpc2U6IHNvcnQgYnkgY3RpbWUKAAAA +AAAAAAAAAAAAAAAAAAAAAAAAICAtQyAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0IGVudHJp +ZXMgYnkgY29sdW1ucwogICAgICAtLWNvbG9yWz1XSEVOXSAgICAgICAgIGNvbnRyb2wgd2hldGhl +ciBjb2xvciBpcyB1c2VkIHRvIGRpc3Rpbmd1aXNoIGZpbGUKICAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgIHR5cGVzLiAgV0hFTiBtYXkgYmUgYG5ldmVyJywgYGFsd2F5cycsIG9yIGBhdXRv +JwogIC1kLCAtLWRpcmVjdG9yeSAgICAgICAgICAgIGxpc3QgZGlyZWN0b3J5IGVudHJpZXMgaW5z +dGVhZCBvZiBjb250ZW50cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuZCBkbyBu +b3QgZGVyZWZlcmVuY2Ugc3ltYm9saWMgbGlua3MKICAtRCwgLS1kaXJlZCAgICAgICAgICAgICAg +ICBnZW5lcmF0ZSBvdXRwdXQgZGVzaWduZWQgZm9yIEVtYWNzJyBkaXJlZCBtb2RlCgAAAAAAAAAA +AAAAAAAAAAAAACAgLWYgICAgICAgICAgICAgICAgICAgICAgICAgZG8gbm90IHNvcnQsIGVuYWJs +ZSAtYVUsIGRpc2FibGUgLWxzdAogIC1GLCAtLWNsYXNzaWZ5ICAgICAgICAgICAgIGFwcGVuZCBp +bmRpY2F0b3IgKG9uZSBvZiAqLz1AfCkgdG8gZW50cmllcwogICAgICAtLWZvcm1hdD1XT1JEICAg +ICAgICAgIGFjcm9zcyAteCwgY29tbWFzIC1tLCBob3Jpem9udGFsIC14LCBsb25nIC1sLAogICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgc2luZ2xlLWNvbHVtbiAtMSwgdmVyYm9zZSAtbCwg +dmVydGljYWwgLUMKICAgICAgLS1mdWxsLXRpbWUgICAgICAgICAgICBsaWtlIC1sIC0tdGltZS1z +dHlsZT1mdWxsLWlzbwoAAAAAAAAgIC1nICAgICAgICAgICAgICAgICAgICAgICAgIGxpa2UgLWws +IGJ1dCBkbyBub3QgbGlzdCBvd25lcgogIC1HLCAtLW5vLWdyb3VwICAgICAgICAgICAgIGluaGli +aXQgZGlzcGxheSBvZiBncm91cCBpbmZvcm1hdGlvbgogIC1oLCAtLWh1bWFuLXJlYWRhYmxlICBw +cmludCBzaXplcyBpbiBodW1hbiByZWFkYWJsZSBmb3JtYXQgKGUuZy4sIDFLIDIzNE0gMkcpCiAg +ICAgIC0tc2kgICAgICAgICAgICAgICAgICAgbGlrZXdpc2UsIGJ1dCB1c2UgcG93ZXJzIG9mIDEw +MDAgbm90IDEwMjQKICAtSCwgLS1kZXJlZmVyZW5jZS1jb21tYW5kLWxpbmUKICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICBmb2xsb3cgc3ltYm9saWMgbGlua3MgbGlzdGVkIG9uIHRoZSBjb21t +YW5kIGxpbmUKICAgICAgLS1kZXJlZmVyZW5jZS1jb21tYW5kLWxpbmUtc3ltbGluay10by1kaXIK +ICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb2xsb3cgZWFjaCBjb21tYW5kIGxpbmUgc3lt +Ym9saWMgbGluawogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhhdCBwb2ludHMgdG8g +YSBkaXJlY3RvcnkKAAAAAAAAAAAAAAAAAAAgICAgICAtLWluZGljYXRvci1zdHlsZT1XT1JEIGFw +cGVuZCBpbmRpY2F0b3Igd2l0aCBzdHlsZSBXT1JEIHRvIGVudHJ5IG5hbWVzOgogICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgbm9uZSAoZGVmYXVsdCksIGNsYXNzaWZ5ICgtRiksIGZpbGUt +dHlwZSAoLXApCiAgLWksIC0taW5vZGUgICAgICAgICAgICAgICAgcHJpbnQgaW5kZXggbnVtYmVy +IG9mIGVhY2ggZmlsZQogIC1JLCAtLWlnbm9yZT1QQVRURVJOICAgICAgIGRvIG5vdCBsaXN0IGlt +cGxpZWQgZW50cmllcyBtYXRjaGluZyBzaGVsbCBQQVRURVJOCiAgLWsgICAgICAgICAgICAgICAg +ICAgICAgICAgbGlrZSAtLWJsb2NrLXNpemU9MUsKAAAAAAAAICAtbCAgICAgICAgICAgICAgICAg +ICAgICAgICB1c2UgYSBsb25nIGxpc3RpbmcgZm9ybWF0CiAgLUwsIC0tZGVyZWZlcmVuY2UgICAg +ICAgICAgd2hlbiBzaG93aW5nIGZpbGUgaW5mb3JtYXRpb24gZm9yIGEgc3ltYm9saWMKICAgICAg +ICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmssIHNob3cgaW5mb3JtYXRpb24gZm9yIHRoZSBm +aWxlIHRoZSBsaW5rCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWZlcmVuY2VzIHJh +dGhlciB0aGFuIGZvciB0aGUgbGluayBpdHNlbGYKICAtbSAgICAgICAgICAgICAgICAgICAgICAg +ICBmaWxsIHdpZHRoIHdpdGggYSBjb21tYSBzZXBhcmF0ZWQgbGlzdCBvZiBlbnRyaWVzCgAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAICAtbiwgLS1udW1lcmljLXVpZC1naWQgICAgICBsaWtlIC1s +LCBidXQgbGlzdCBudW1lcmljIFVJRHMgYW5kIEdJRHMKICAtTiwgLS1saXRlcmFsICAgICAgICAg +ICAgICBwcmludCByYXcgZW50cnkgbmFtZXMgKGRvbid0IHRyZWF0IGUuZy4gY29udHJvbAogICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hhcmFjdGVycyBzcGVjaWFsbHkpCiAgLW8gICAg +ICAgICAgICAgICAgICAgICAgICAgbGlrZSAtbCwgYnV0IGRvIG5vdCBsaXN0IGdyb3VwIGluZm9y +bWF0aW9uCiAgLXAsIC0tZmlsZS10eXBlICAgICAgICAgICAgYXBwZW5kIGluZGljYXRvciAob25l +IG9mIC89QHwpIHRvIGVudHJpZXMKAAAAAAAAAAAAACAgLXEsIC0taGlkZS1jb250cm9sLWNoYXJz +ICAgcHJpbnQgPyBpbnN0ZWFkIG9mIG5vbiBncmFwaGljIGNoYXJhY3RlcnMKICAgICAgLS1zaG93 +LWNvbnRyb2wtY2hhcnMgICBzaG93IG5vbiBncmFwaGljIGNoYXJhY3RlcnMgYXMtaXMgKGRlZmF1 +bHQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bmxlc3MgcHJvZ3JhbSBpcyBgbHMnIGFu +ZCBvdXRwdXQgaXMgYSB0ZXJtaW5hbCkKICAtUSwgLS1xdW90ZS1uYW1lICAgICAgICAgICBlbmNs +b3NlIGVudHJ5IG5hbWVzIGluIGRvdWJsZSBxdW90ZXMKICAgICAgLS1xdW90aW5nLXN0eWxlPVdP +UkQgICB1c2UgcXVvdGluZyBzdHlsZSBXT1JEIGZvciBlbnRyeSBuYW1lczoKICAgICAgICAgICAg +ICAgICAgICAgICAgICAgICAgIGxpdGVyYWwsIGxvY2FsZSwgc2hlbGwsIHNoZWxsLWFsd2F5cywg +YywgZXNjYXBlCgAAAAAAAAAAAAAAAAAgIC1yLCAtLXJldmVyc2UgICAgICAgICAgICAgIHJldmVy +c2Ugb3JkZXIgd2hpbGUgc29ydGluZwogIC1SLCAtLXJlY3Vyc2l2ZSAgICAgICAgICAgIGxpc3Qg +c3ViZGlyZWN0b3JpZXMgcmVjdXJzaXZlbHkKICAtcywgLS1zaXplICAgICAgICAgICAgICAgICBw +cmludCBzaXplIG9mIGVhY2ggZmlsZSwgaW4gYmxvY2tzCgAAAAAAAAAAAAAgIC1TICAgICAgICAg +ICAgICAgICAgICAgICAgIHNvcnQgYnkgZmlsZSBzaXplCiAgICAgIC0tc29ydD1XT1JEICAgICAg +ICAgICAgZXh0ZW5zaW9uIC1YLCBub25lIC1VLCBzaXplIC1TLCB0aW1lIC10LAogICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICAgdmVyc2lvbiAtdgogICAgICAgICAgICAgICAgICAgICAgICAg +ICAgIHN0YXR1cyAtYywgdGltZSAtdCwgYXRpbWUgLXUsIGFjY2VzcyAtdSwgdXNlIC11CiAgICAg +IC0tdGltZT1XT1JEICAgICAgICAgICAgc2hvdyB0aW1lIGFzIFdPUkQgaW5zdGVhZCBvZiBtb2Rp +ZmljYXRpb24gdGltZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF0aW1lLCBhY2Nl +c3MsIHVzZSwgY3RpbWUgb3Igc3RhdHVzOyB1c2UKICAgICAgICAgICAgICAgICAgICAgICAgICAg +ICAgIHNwZWNpZmllZCB0aW1lIGFzIHNvcnQga2V5IGlmIC0tc29ydD10aW1lCgAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAgICAgICAtLXRpbWUtc3R5bGU9U1RZTEUgICAgIHNob3cgdGltZXMgdXNpbmcg +c3R5bGUgU1RZTEU6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLWlzbywgbG9u +Zy1pc28sIGlzbywgbG9jYWxlLCArRk9STUFUCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAg +Rk9STUFUIGlzIGludGVycHJldGVkIGxpa2UgYGRhdGUnOyBpZiBGT1JNQVQgaXMKICAgICAgICAg +ICAgICAgICAgICAgICAgICAgICBGT1JNQVQxPG5ld2xpbmU+Rk9STUFUMiwgRk9STUFUMSBhcHBs +aWVzIHRvCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9uLXJlY2VudCBmaWxlcyBhbmQg +Rk9STUFUMiB0byByZWNlbnQgZmlsZXM7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYg +U1RZTEUgaXMgcHJlZml4ZWQgd2l0aCBgcG9zaXgtJywgU1RZTEUKICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICB0YWtlcyBlZmZlY3Qgb25seSBvdXRzaWRlIHRoZSBQT1NJWCBsb2NhbGUKICAt +dCAgICAgICAgICAgICAgICAgICAgICAgICBzb3J0IGJ5IG1vZGlmaWNhdGlvbiB0aW1lCiAgLVQs +IC0tdGFic2l6ZT1DT0xTICAgICAgICAgYXNzdW1lIHRhYiBzdG9wcyBhdCBlYWNoIENPTFMgaW5z +dGVhZCBvZiA4CgAAAAAAAAAAAAAAAAAAICAtdSAgICAgICAgICAgICAgICAgICAgICAgICB3aXRo +IC1sdDogc29ydCBieSwgYW5kIHNob3csIGFjY2VzcyB0aW1lCiAgICAgICAgICAgICAgICAgICAg +ICAgICAgICAgICB3aXRoIC1sOiBzaG93IGFjY2VzcyB0aW1lIGFuZCBzb3J0IGJ5IG5hbWUKICAg +ICAgICAgICAgICAgICAgICAgICAgICAgICAgIG90aGVyd2lzZTogc29ydCBieSBhY2Nlc3MgdGlt +ZQogIC1VICAgICAgICAgICAgICAgICAgICAgICAgIGRvIG5vdCBzb3J0OyBsaXN0IGVudHJpZXMg +aW4gZGlyZWN0b3J5IG9yZGVyCiAgLXYgICAgICAgICAgICAgICAgICAgICAgICAgc29ydCBieSB2 +ZXJzaW9uCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgLXcsIC0td2lkdGg9Q09MUyAgICAg +ICAgICAgYXNzdW1lIHNjcmVlbiB3aWR0aCBpbnN0ZWFkIG9mIGN1cnJlbnQgdmFsdWUKICAteCAg +ICAgICAgICAgICAgICAgICAgICAgICBsaXN0IGVudHJpZXMgYnkgbGluZXMgaW5zdGVhZCBvZiBi +eSBjb2x1bW5zCiAgLVggICAgICAgICAgICAgICAgICAgICAgICAgc29ydCBhbHBoYWJldGljYWxs +eSBieSBlbnRyeSBleHRlbnNpb24KICAtMSAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0IG9u +ZSBmaWxlIHBlciBsaW5lCgAAAAAAAAAAAAAAAAAAAAAAAAAAACAgICAgIC0taGVscCAgICAgZGlz +cGxheSB0aGlzIGhlbHAgYW5kIGV4aXQKAAAAAAAAAAAAAAAAAAAAAAAAAAAgICAgICAtLXZlcnNp +b24gIG91dHB1dCB2ZXJzaW9uIGluZm9ybWF0aW9uIGFuZCBleGl0CgAAAAAAAAAAAAAAClNJWkUg +bWF5IGJlIChvciBtYXkgYmUgYW4gaW50ZWdlciBvcHRpb25hbGx5IGZvbGxvd2VkIGJ5KSBvbmUg +b2YgZm9sbG93aW5nOgprQiAxMDAwLCBLIDEwMjQsIE1CIDEwMDAqMTAwMCwgTSAxMDI0KjEwMjQs +IGFuZCBzbyBvbiBmb3IgRywgVCwgUCwgRSwgWiwgWS4KAAAAAAAAAApCeSBkZWZhdWx0LCBjb2xv +ciBpcyBub3QgdXNlZCB0byBkaXN0aW5ndWlzaCB0eXBlcyBvZiBmaWxlcy4gIFRoYXQgaXMKZXF1 +aXZhbGVudCB0byB1c2luZyAtLWNvbG9yPW5vbmUuICBVc2luZyB0aGUgLS1jb2xvciBvcHRpb24g +d2l0aG91dCB0aGUKb3B0aW9uYWwgV0hFTiBhcmd1bWVudCBpcyBlcXVpdmFsZW50IHRvIHVzaW5n +IC0tY29sb3I9YWx3YXlzLiAgV2l0aAotLWNvbG9yPWF1dG8sIGNvbG9yIGNvZGVzIGFyZSBvdXRw +dXQgb25seSBpZiBzdGFuZGFyZCBvdXRwdXQgaXMgY29ubmVjdGVkCnRvIGEgdGVybWluYWwgKHR0 +eSkuCgBwcmVzZXJ2aW5nIHBlcm1pc3Npb25zIGZvciAlcwB1OjotLS0sZzo6LS0tLG86Oi0tLQBz +ZXR0aW5nIHBlcm1pc3Npb25zIGZvciAlcwAlbS8lZC8leQAlWS0lbS0lZAAlSDolTTolUwDy+QQI +APkECAD5BAgA+QQIAPkECAD5BAgA+QQIAPkECAD5BAgA+QQIAPkECAD5BAgA+QQIAPkECAD5BAgA ++QQIAPkECAD5BAgA+QQIAPkECAD5BAgA+QQIAPkECAD5BAgA+QQIAPkECAD5BAgA+QQIAPkECAD5 +BAgA+QQIAPkECAD5BAgA+QQIAPkECAD5BAgA+QQIVPgECAD5BAgA+QQIAPkECAD5BAgA+QQIAPkE +CAD5BAgA+QQIAPkECAD5BAgA+QQIAPkECAD5BAgA+QQIAPkECAD5BAgA+QQIAPkECAD5BAgA+QQI +APkECAD5BAgA+QQIAPkECAD5BAgA+QQIAPkECP35BAj9+QQIlfsECFb+BAgA+QQI0f8ECOEDBQjh +/wQIEAAFCAD5BAgA+QQIAPkECEAABQhwAAUIAPkECMgABQgA+QQI/gAFCA4BBQg3AQUIRwEFCOED +BQiQAQUI8AEFCAICBQhAAgUIAPkECAD5BAgA+QQIAPkECAD5BAgA+QQI/fkECMMGBQjwAQUIaAMF +CJIDBQgA+QQI4QMFCMMGBQgA+QQI7QYFCBYHBQhABwUIcAcFCKAHBQgA+QQI3gAFCAD5BAgi+gQI +MAgFCJwIBQglCQUIAPkECGcJBQjwAQUIkQkFCOcJBQgAAAAAAACAP83MTD/0/bQ/AAAAACMgZW50 +cmllczogICAgICAgICAldQoAIyBidWNrZXRzOiAgICAgICAgICV1CgBtYXggYnVja2V0IGxlbmd0 +aDogJXUKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjIGJ1Y2tldHMgdXNlZDogICAg +JXUgKCUuMmYlJSkKAAAAyEJhbWJpZ3VvdXMgYXJndW1lbnQgJXMgZm9yICVzAGludmFsaWQgYXJn +dW1lbnQgJXMgZm9yICVzAFZhbGlkIGFyZ3VtZW50cyBhcmU6AAogIC0gYCVzJwAsIGAlcycAd3Jp +dGUgZXJyb3IAJXM6ICVzAFBPU0lYAAAAS01HVFBFWlkAAACqWgUIHVsFCAAAAABwAAAAUAAAAAAA +AAD//////////z5AAAAAAAAAAAAAAAAAAKACQAAAAAAAAAAAgF8lLjFMZgAlLjBMZgBlRWdHa0tt +TXBQdFR5WXpaMABibG9jayBzaXplACVzIGAlcycgdG9vIGxhcmdlAGludmFsaWQgJXMgYCVzJwAA +AAAAAAAAAAAAAAAAAAAAAGludmFsaWQgY2hhcmFjdGVyIGZvbGxvd2luZyAlcyBpbiBgJXMnAHNo +ZWxsAHNoZWxsLWFsd2F5cwBjbG9jYWxlACIAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExb +BQhniwUIbYsFCNaPBQiJWgUIW1wFCHqLBQgAAAAAAAAAAAEAAAACAAAAAwAAAAQAAAAFAAAABgAA +AJ41BQieNQUIzzoFCHQ1BQjwOgUI/DoFCPw6BQgwNgUIZDgFCGQ4BQhkOAUIZDgFCGQ4BQhkOAUI +ADcFCCI3BQgmNwUIZjcFCGo3BQhuNwUIcjcFCGQ4BQhkOAUIZDgFCGQ4BQhkOAUIZDgFCGQ4BQhk +OAUIZDgFCGQ4BQhkOAUIZDgFCGQ4BQhkOAUIZDgFCGQ4BQhkOAUIZDgFCFU6BQhVOgUIVToFCGQ6 +BQhVOgUIkDYFCFU6BQh2NwUIVToFCFU6BQhVOgUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYF +CJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYFCFU6BQhVOgUIkDYFCFU6BQjKNwUI +ZDgFCJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQ +NgUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQNgUIVToFCEw6 +BQiQNgUIVToFCJA2BQhVOgUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYF +CJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQNgUI +kDYFCJA2BQiQNgUIVToFCJA2BQhkOgUIGTgFCJA2BQiQNgUIkDYFCJA2BQiQNgUIGTgFCBk4BQgZ +OAUIkDYFCJA2BQiQNgUIGTgFCJA2BQgZOAUIkDYFCJA2BQiQNgUIkDYFCJA2BQiQNgUIkDYFCJA2 +BQiQNgUIkDYFCJA2BQiQNgUIGTgFCBk4BQgZOAUIAAAAAAAAAAAAAAAAAAAAAAAAAABDb3B5cmln +aHQgKEMpIDIwMDMgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBJbmMuAAAAAAAAAAAAAAAAAAAA +VGhpcyBpcyBmcmVlIHNvZnR3YXJlOyBzZWUgdGhlIHNvdXJjZSBmb3IgY29weWluZyBjb25kaXRp +b25zLiAgVGhlcmUgaXMgTk8Kd2FycmFudHk7IG5vdCBldmVuIGZvciBNRVJDSEFOVEFCSUxJVFkg +b3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuCgAlcyAoJXMpICVzCgBXcml0dGVu +IGJ5ICVzLgoAJXMgJXMKAG1lbW9yeSBleGhhdXN0ZWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAwIDw9 +IHN0cnRvbF9iYXNlICYmIHN0cnRvbF9iYXNlIDw9IDM2AHhzdHJ0b2wAeHN0cnRvbC5jAOJEBQhO +RQUITkUFCO1EBQhORQUILkUFCE5FBQhORQUITkUFCDVFBQhORQUIPEUFCE5FBQhORQUIFUUFCE5F +BQhORQUITkUFCENFBQhORQUITkUFCE5FBQhORQUIHEUFCCdFBQhORQUITkUFCE5FBQhORQUITkUF +CE5FBQhORQUIpkQFCMtEBQhORQUITkUFCE5FBQguRQUITkUFCE5FBQhORQUINUUFCE5FBQg8RQUI +TkUFCE5FBQhORQUITkUFCE5FBQhORQUIQ0UFCE5FBQhORQUIYEUFCAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHhzdHJ0b3VtYXgAAAAARUgFCKNI +BQijSAUIUEgFCKNIBQiDSAUIo0gFCKNIBQijSAUIikgFCKNIBQiRSAUIo0gFCKNIBQhqSAUIo0gF +CKNIBQijSAUImEgFCKNIBQijSAUIo0gFCKNIBQhxSAUIfEgFCKNIBQijSAUIo0gFCKNIBQijSAUI +o0gFCKNIBQgJSAUIKEgFCKNIBQijSAUIo0gFCINIBQijSAUIo0gFCKNIBQiKSAUIo0gFCJFIBQij +SAUIo0gFCKNIBQijSAUIo0gFCKNIBQiYSAUIo0gFCKNIBQi1SAUIAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAB8AOwBaAHgAlwC1ANQA8wARATABTgFtAQAAHwA8AFsAeQCYALYA1QD0ABIBMQFPAW4BAAAA +AAAAgC8BGwM7KBEAAAQAAAD0v///RBEAAITB//9kEQAAlMP//4QRAADExP//pBEAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyKQFCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgP////8B +AAAAAQAAAAEAAAAAAAAAAAAAAAAAAAACAAAAUFoFCAEAAABTWgUIAAAAAAAAAAABAAAAAIsFCAEA +AAAAiwUIBQAAAFVaBQgFAAAAW1oFCAIAAABqWgUIBQAAAGFaBQgFAAAAZ1oFCAUAAABnWgUIAAAA +AAAAAAAAAAAAAAAAAAUAAABtWgUIBQAAAGFaBQhzWgUIfVoFCAEAAAD/////AQAAACAZBQgBAAAA +AQAAAAABAADAqAUIOKMFCICOBQgUAAAAAAAAAAF6UgABfAgBGwwEBIgBAAAcAAAAHAAAAKiu//+D +AQAAAEEOCIUCQg0FRYYEhwMAABwAAAA8AAAAGLD//wMCAAAAQQ4IhQJCDQVFhgSHAwAAHAAAAFwA +AAAIsv//LQEAAABBDgiFAkINBUWGBIcDAAAcAAAAfAAAABiz//+7AQAAAEEOCIUCQg0FSIYEhwMA +AAAAAAABAAAAAQAAAAEAAAA9AAAAAQAAANcAAAAMAAAAsJMECA0AAADQWQUIBAAAAEiBBAgFAAAA +4IoECAYAAABwhAQICgAAAGoEAAALAAAAEAAAABUAAAAAAAAAAwAAANCkBQgCAAAAwAIAABQAAAAR +AAAAFwAAAPCQBAgRAAAAyJAECBIAAAAoAAAAEwAAAAgAAAD+//9vGJAECP///28DAAAA8P//b0qP +BAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////AAAA +AP////8AAAAAAAAAAOSjBQgAAAAAAAAAAN6TBAjukwQI/pMECA6UBAgelAQILpQECD6UBAhOlAQI +XpQECG6UBAh+lAQIjpQECJ6UBAiulAQIvpQECM6UBAjelAQI7pQECP6UBAgOlQQIHpUECC6VBAg+ +lQQITpUECF6VBAhulQQIfpUECI6VBAielQQIrpUECL6VBAjOlQQI3pUECO6VBAj+lQQIDpYECB6W +BAgulgQIPpYECE6WBAhelgQIbpYECH6WBAiOlgQInpYECK6WBAi+lgQIzpYECN6WBAjulgQI/pYE +CA6XBAgelwQILpcECD6XBAhOlwQIXpcECG6XBAh+lwQIjpcECJ6XBAiulwQIvpcECM6XBAjelwQI +7pcECP6XBAgOmAQIHpgECC6YBAg+mAQITpgECF6YBAhumAQIfpgECI6YBAiemAQIrpgECL6YBAjO +mAQI3pgECO6YBAj+mAQIDpkECB6ZBAgumQQIPpkECE6ZBAhkogUIAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAALnNoc3RydGFiAC5pbnRlcnAALm5vdGUuQUJJLXRhZwAuaGFzaAAuZHlu +c3ltAC5keW5zdHIALmdudS52ZXJzaW9uAC5nbnUudmVyc2lvbl9yAC5yZWwuZHluAC5yZWwucGx0 +AC5pbml0AC50ZXh0AC5maW5pAC5yb2RhdGEALmVoX2ZyYW1lX2hkcgAuZGF0YQAuZWhfZnJhbWUA +LmR5bmFtaWMALmN0b3JzAC5kdG9ycwAuamNyAC5nb3QALmJzcwAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAABAAAAAgAAABSBBAgUAQAAEwAAAAAAAAAAAAAAAQAA +AAAAAAATAAAABwAAAAIAAAAogQQIKAEAACAAAAAAAAAAAAAAAAQAAAAAAAAAIQAAAAUAAAACAAAA +SIEECEgBAAAoAwAABAAAAAAAAAAEAAAABAAAACcAAAALAAAAAgAAAHCEBAhwBAAAcAYAAAUAAAAB +AAAABAAAABAAAAAvAAAAAwAAAAIAAADgigQI4AoAAGoEAAAAAAAAAAAAAAEAAAAAAAAANwAAAP// +/28CAAAASo8ECEoPAADOAAAABAAAAAAAAAACAAAAAgAAAEQAAAD+//9vAgAAABiQBAgYEAAAsAAA +AAUAAAADAAAABAAAAAAAAABTAAAACQAAAAIAAADIkAQIyBAAACgAAAAEAAAAAAAAAAQAAAAIAAAA +XAAAAAkAAAACAAAA8JAECPAQAADAAgAABAAAAAsAAAAEAAAACAAAAGUAAAABAAAABgAAALCTBAiw +EwAAFwAAAAAAAAAAAAAABAAAAAAAAABgAAAAAQAAAAYAAADIkwQIyBMAAJAFAAAAAAAAAAAAAAQA +AAAEAAAAawAAAAEAAAAGAAAAYJkECGAZAABwwAAAAAAAAAAAAAAQAAAAAAAAAHEAAAABAAAABgAA +ANBZBQjQ2QAAGwAAAAAAAAAAAAAABAAAAAAAAAB3AAAAAQAAAAIAAAAAWgUIANoAABw4AAAAAAAA +AAAAACAAAAAAAAAAfwAAAAEAAAACAAAAHJIFCBwSAQAsAAAAAAAAAAAAAAAEAAAAAAAAAI0AAAAB +AAAAAwAAAGCiBQhgEgEA6AAAAAAAAAAAAAAAIAAAAAAAAACTAAAAAQAAAAIAAABIowUISBMBAJwA +AAAAAAAAAAAAAAQAAAAAAAAAnQAAAAYAAAADAAAA5KMFCOQTAQDYAAAABQAAAAAAAAAEAAAACAAA +AKYAAAABAAAAAwAAALykBQi8FAEACAAAAAAAAAAAAAAABAAAAAAAAACtAAAAAQAAAAMAAADEpAUI +xBQBAAgAAAAAAAAAAAAAAAQAAAAAAAAAtAAAAAEAAAADAAAAzKQFCMwUAQAEAAAAAAAAAAAAAAAE +AAAAAAAAALkAAAABAAAAAwAAANCkBQjQFAEAdAEAAAAAAAAAAAAABAAAAAQAAAC+AAAACAAAAAMA +AABgpgUIYBYBAJAD +AA AAAAAAAAAAACAAAAAAAAA AAQAAAAMAAAAAAAAAAAAAAGAWAQDDAAAAAAAA +AAAAAAABAAAAAAAAAA== diff --git a/tests/encoding/test-suites/encode/1byte b/tests/encoding/test-suites/encode/1byte new file mode 100644 index 00000000..f70f10e4 --- /dev/null +++ b/tests/encoding/test-suites/encode/1byte @@ -0,0 +1 @@ +A diff --git a/tests/encoding/test-suites/encode/1byte.base64 b/tests/encoding/test-suites/encode/1byte.base64 new file mode 100644 index 00000000..b3b4e952 --- /dev/null +++ b/tests/encoding/test-suites/encode/1byte.base64 @@ -0,0 +1 @@ +QQo= \ No newline at end of file diff --git a/tests/encoding/test-suites/encode/2bytes b/tests/encoding/test-suites/encode/2bytes new file mode 100644 index 00000000..3bd504ce --- /dev/null +++ b/tests/encoding/test-suites/encode/2bytes @@ -0,0 +1 @@ +AB diff --git a/tests/encoding/test-suites/encode/2bytes.base64 b/tests/encoding/test-suites/encode/2bytes.base64 new file mode 100644 index 00000000..5fba948e --- /dev/null +++ b/tests/encoding/test-suites/encode/2bytes.base64 @@ -0,0 +1 @@ +QUIK \ No newline at end of file diff --git a/tests/encoding/test-suites/encode/3bytes b/tests/encoding/test-suites/encode/3bytes new file mode 100644 index 00000000..5da849b5 --- /dev/null +++ b/tests/encoding/test-suites/encode/3bytes @@ -0,0 +1 @@ +ABC diff --git a/tests/encoding/test-suites/encode/3bytes.base64 b/tests/encoding/test-suites/encode/3bytes.base64 new file mode 100644 index 00000000..82381778 --- /dev/null +++ b/tests/encoding/test-suites/encode/3bytes.base64 @@ -0,0 +1 @@ +QUJDCg== \ No newline at end of file diff --git a/tests/encoding/test-suites/encode/empty b/tests/encoding/test-suites/encode/empty new file mode 100644 index 00000000..e69de29b diff --git a/tests/encoding/test-suites/encode/empty.base64 b/tests/encoding/test-suites/encode/empty.base64 new file mode 100644 index 00000000..e69de29b diff --git a/tests/encoding/test-suites/encode/empty.quoted-printable b/tests/encoding/test-suites/encode/empty.quoted-printable new file mode 100644 index 00000000..e69de29b diff --git a/tests/encoding/test-suites/encode/gpl b/tests/encoding/test-suites/encode/gpl new file mode 100644 index 00000000..0128b1ba --- /dev/null +++ b/tests/encoding/test-suites/encode/gpl @@ -0,0 +1,140 @@ +GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + * a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + + * b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + + * c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + + * a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + * b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + + * c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + one line to give the program's name and an idea of what it does. + Copyright (C) yyyy name of author + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details + type `show w'. This is free software, and you are welcome + to redistribute it under certain conditions; type `show c' + for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright + interest in the program `Gnomovision' + (which makes passes at compilers) written + by James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. diff --git a/tests/encoding/test-suites/encode/gpl.base64 b/tests/encoding/test-suites/encode/gpl.base64 new file mode 100644 index 00000000..0bbd19a0 --- /dev/null +++ b/tests/encoding/test-suites/encode/gpl.base64 @@ -0,0 +1,311 @@ +R05VIEdFTkVSQUwgUFVCTElDIExJQ0VOU0UKClZlcnNpb24gMiwgSnVuZSAxOTkxCgpDb3B5cmln +aHQgKEMpIDE5ODksIDE5OTEgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBJbmMuICAKNTkgVGVt +cGxlIFBsYWNlIC0gU3VpdGUgMzMwLCBCb3N0b24sIE1BICAwMjExMS0xMzA3LCBVU0EKCkV2ZXJ5 +b25lIGlzIHBlcm1pdHRlZCB0byBjb3B5IGFuZCBkaXN0cmlidXRlIHZlcmJhdGltIGNvcGllcwpv +ZiB0aGlzIGxpY2Vuc2UgZG9jdW1lbnQsIGJ1dCBjaGFuZ2luZyBpdCBpcyBub3QgYWxsb3dlZC4K +ClByZWFtYmxlCgpUaGUgbGljZW5zZXMgZm9yIG1vc3Qgc29mdHdhcmUgYXJlIGRlc2lnbmVkIHRv +IHRha2UgYXdheSB5b3VyIGZyZWVkb20gdG8gc2hhcmUgYW5kIGNoYW5nZSBpdC4gQnkgY29udHJh +c3QsIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBpcyBpbnRlbmRlZCB0byBndWFyYW50 +ZWUgeW91ciBmcmVlZG9tIHRvIHNoYXJlIGFuZCBjaGFuZ2UgZnJlZSBzb2Z0d2FyZS0tdG8gbWFr +ZSBzdXJlIHRoZSBzb2Z0d2FyZSBpcyBmcmVlIGZvciBhbGwgaXRzIHVzZXJzLiBUaGlzIEdlbmVy +YWwgUHVibGljIExpY2Vuc2UgYXBwbGllcyB0byBtb3N0IG9mIHRoZSBGcmVlIFNvZnR3YXJlIEZv +dW5kYXRpb24ncyBzb2Z0d2FyZSBhbmQgdG8gYW55IG90aGVyIHByb2dyYW0gd2hvc2UgYXV0aG9y +cyBjb21taXQgdG8gdXNpbmcgaXQuIChTb21lIG90aGVyIEZyZWUgU29mdHdhcmUgRm91bmRhdGlv +biBzb2Z0d2FyZSBpcyBjb3ZlcmVkIGJ5IHRoZSBHTlUgTGlicmFyeSBHZW5lcmFsIFB1YmxpYyBM +aWNlbnNlIGluc3RlYWQuKSBZb3UgY2FuIGFwcGx5IGl0IHRvIHlvdXIgcHJvZ3JhbXMsIHRvby4K +CldoZW4gd2Ugc3BlYWsgb2YgZnJlZSBzb2Z0d2FyZSwgd2UgYXJlIHJlZmVycmluZyB0byBmcmVl +ZG9tLCBub3QgcHJpY2UuIE91ciBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlcyBhcmUgZGVzaWduZWQg +dG8gbWFrZSBzdXJlIHRoYXQgeW91IGhhdmUgdGhlIGZyZWVkb20gdG8gZGlzdHJpYnV0ZSBjb3Bp +ZXMgb2YgZnJlZSBzb2Z0d2FyZSAoYW5kIGNoYXJnZSBmb3IgdGhpcyBzZXJ2aWNlIGlmIHlvdSB3 +aXNoKSwgdGhhdCB5b3UgcmVjZWl2ZSBzb3VyY2UgY29kZSBvciBjYW4gZ2V0IGl0IGlmIHlvdSB3 +YW50IGl0LCB0aGF0IHlvdSBjYW4gY2hhbmdlIHRoZSBzb2Z0d2FyZSBvciB1c2UgcGllY2VzIG9m +IGl0IGluIG5ldyBmcmVlIHByb2dyYW1zOyBhbmQgdGhhdCB5b3Uga25vdyB5b3UgY2FuIGRvIHRo +ZXNlIHRoaW5ncy4KClRvIHByb3RlY3QgeW91ciByaWdodHMsIHdlIG5lZWQgdG8gbWFrZSByZXN0 +cmljdGlvbnMgdGhhdCBmb3JiaWQgYW55b25lIHRvIGRlbnkgeW91IHRoZXNlIHJpZ2h0cyBvciB0 +byBhc2sgeW91IHRvIHN1cnJlbmRlciB0aGUgcmlnaHRzLiBUaGVzZSByZXN0cmljdGlvbnMgdHJh +bnNsYXRlIHRvIGNlcnRhaW4gcmVzcG9uc2liaWxpdGllcyBmb3IgeW91IGlmIHlvdSBkaXN0cmli +dXRlIGNvcGllcyBvZiB0aGUgc29mdHdhcmUsIG9yIGlmIHlvdSBtb2RpZnkgaXQuCgpGb3IgZXhh +bXBsZSwgaWYgeW91IGRpc3RyaWJ1dGUgY29waWVzIG9mIHN1Y2ggYSBwcm9ncmFtLCB3aGV0aGVy +IGdyYXRpcyBvciBmb3IgYSBmZWUsIHlvdSBtdXN0IGdpdmUgdGhlIHJlY2lwaWVudHMgYWxsIHRo +ZSByaWdodHMgdGhhdCB5b3UgaGF2ZS4gWW91IG11c3QgbWFrZSBzdXJlIHRoYXQgdGhleSwgdG9v +LCByZWNlaXZlIG9yIGNhbiBnZXQgdGhlIHNvdXJjZSBjb2RlLiBBbmQgeW91IG11c3Qgc2hvdyB0 +aGVtIHRoZXNlIHRlcm1zIHNvIHRoZXkga25vdyB0aGVpciByaWdodHMuCgpXZSBwcm90ZWN0IHlv +dXIgcmlnaHRzIHdpdGggdHdvIHN0ZXBzOiAoMSkgY29weXJpZ2h0IHRoZSBzb2Z0d2FyZSwgYW5k +ICgyKSBvZmZlciB5b3UgdGhpcyBsaWNlbnNlIHdoaWNoIGdpdmVzIHlvdSBsZWdhbCBwZXJtaXNz +aW9uIHRvIGNvcHksIGRpc3RyaWJ1dGUgYW5kL29yIG1vZGlmeSB0aGUgc29mdHdhcmUuCgpBbHNv +LCBmb3IgZWFjaCBhdXRob3IncyBwcm90ZWN0aW9uIGFuZCBvdXJzLCB3ZSB3YW50IHRvIG1ha2Ug +Y2VydGFpbiB0aGF0IGV2ZXJ5b25lIHVuZGVyc3RhbmRzIHRoYXQgdGhlcmUgaXMgbm8gd2FycmFu +dHkgZm9yIHRoaXMgZnJlZSBzb2Z0d2FyZS4gSWYgdGhlIHNvZnR3YXJlIGlzIG1vZGlmaWVkIGJ5 +IHNvbWVvbmUgZWxzZSBhbmQgcGFzc2VkIG9uLCB3ZSB3YW50IGl0cyByZWNpcGllbnRzIHRvIGtu +b3cgdGhhdCB3aGF0IHRoZXkgaGF2ZSBpcyBub3QgdGhlIG9yaWdpbmFsLCBzbyB0aGF0IGFueSBw +cm9ibGVtcyBpbnRyb2R1Y2VkIGJ5IG90aGVycyB3aWxsIG5vdCByZWZsZWN0IG9uIHRoZSBvcmln +aW5hbCBhdXRob3JzJyByZXB1dGF0aW9ucy4KCkZpbmFsbHksIGFueSBmcmVlIHByb2dyYW0gaXMg +dGhyZWF0ZW5lZCBjb25zdGFudGx5IGJ5IHNvZnR3YXJlIHBhdGVudHMuIFdlIHdpc2ggdG8gYXZv +aWQgdGhlIGRhbmdlciB0aGF0IHJlZGlzdHJpYnV0b3JzIG9mIGEgZnJlZSBwcm9ncmFtIHdpbGwg +aW5kaXZpZHVhbGx5IG9idGFpbiBwYXRlbnQgbGljZW5zZXMsIGluIGVmZmVjdCBtYWtpbmcgdGhl +IHByb2dyYW0gcHJvcHJpZXRhcnkuIFRvIHByZXZlbnQgdGhpcywgd2UgaGF2ZSBtYWRlIGl0IGNs +ZWFyIHRoYXQgYW55IHBhdGVudCBtdXN0IGJlIGxpY2Vuc2VkIGZvciBldmVyeW9uZSdzIGZyZWUg +dXNlIG9yIG5vdCBsaWNlbnNlZCBhdCBhbGwuCgpUaGUgcHJlY2lzZSB0ZXJtcyBhbmQgY29uZGl0 +aW9ucyBmb3IgY29weWluZywgZGlzdHJpYnV0aW9uIGFuZCBtb2RpZmljYXRpb24gZm9sbG93LgoK +VEVSTVMgQU5EIENPTkRJVElPTlMgRk9SIENPUFlJTkcsIERJU1RSSUJVVElPTiBBTkQgTU9ESUZJ +Q0FUSU9OCgowLiBUaGlzIExpY2Vuc2UgYXBwbGllcyB0byBhbnkgcHJvZ3JhbSBvciBvdGhlciB3 +b3JrIHdoaWNoIGNvbnRhaW5zIGEgbm90aWNlIHBsYWNlZCBieSB0aGUgY29weXJpZ2h0IGhvbGRl +ciBzYXlpbmcgaXQgbWF5IGJlIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGlzIEdl +bmVyYWwgUHVibGljIExpY2Vuc2UuIFRoZSAiUHJvZ3JhbSIsIGJlbG93LCByZWZlcnMgdG8gYW55 +IHN1Y2ggcHJvZ3JhbSBvciB3b3JrLCBhbmQgYSAid29yayBiYXNlZCBvbiB0aGUgUHJvZ3JhbSIg +bWVhbnMgZWl0aGVyIHRoZSBQcm9ncmFtIG9yIGFueSBkZXJpdmF0aXZlIHdvcmsgdW5kZXIgY29w +eXJpZ2h0IGxhdzogdGhhdCBpcyB0byBzYXksIGEgd29yayBjb250YWluaW5nIHRoZSBQcm9ncmFt +IG9yIGEgcG9ydGlvbiBvZiBpdCwgZWl0aGVyIHZlcmJhdGltIG9yIHdpdGggbW9kaWZpY2F0aW9u +cyBhbmQvb3IgdHJhbnNsYXRlZCBpbnRvIGFub3RoZXIgbGFuZ3VhZ2UuIChIZXJlaW5hZnRlciwg +dHJhbnNsYXRpb24gaXMgaW5jbHVkZWQgd2l0aG91dCBsaW1pdGF0aW9uIGluIHRoZSB0ZXJtICJt +b2RpZmljYXRpb24iLikgRWFjaCBsaWNlbnNlZSBpcyBhZGRyZXNzZWQgYXMgInlvdSIuCgpBY3Rp +dml0aWVzIG90aGVyIHRoYW4gY29weWluZywgZGlzdHJpYnV0aW9uIGFuZCBtb2RpZmljYXRpb24g +YXJlIG5vdCBjb3ZlcmVkIGJ5IHRoaXMgTGljZW5zZTsgdGhleSBhcmUgb3V0c2lkZSBpdHMgc2Nv +cGUuIFRoZSBhY3Qgb2YgcnVubmluZyB0aGUgUHJvZ3JhbSBpcyBub3QgcmVzdHJpY3RlZCwgYW5k +IHRoZSBvdXRwdXQgZnJvbSB0aGUgUHJvZ3JhbSBpcyBjb3ZlcmVkIG9ubHkgaWYgaXRzIGNvbnRl +bnRzIGNvbnN0aXR1dGUgYSB3b3JrIGJhc2VkIG9uIHRoZSBQcm9ncmFtIChpbmRlcGVuZGVudCBv +ZiBoYXZpbmcgYmVlbiBtYWRlIGJ5IHJ1bm5pbmcgdGhlIFByb2dyYW0pLiBXaGV0aGVyIHRoYXQg +aXMgdHJ1ZSBkZXBlbmRzIG9uIHdoYXQgdGhlIFByb2dyYW0gZG9lcy4KCjEuIFlvdSBtYXkgY29w +eSBhbmQgZGlzdHJpYnV0ZSB2ZXJiYXRpbSBjb3BpZXMgb2YgdGhlIFByb2dyYW0ncyBzb3VyY2Ug +Y29kZSBhcyB5b3UgcmVjZWl2ZSBpdCwgaW4gYW55IG1lZGl1bSwgcHJvdmlkZWQgdGhhdCB5b3Ug +Y29uc3BpY3VvdXNseSBhbmQgYXBwcm9wcmlhdGVseSBwdWJsaXNoIG9uIGVhY2ggY29weSBhbiBh +cHByb3ByaWF0ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCBkaXNjbGFpbWVyIG9mIHdhcnJhbnR5OyBr +ZWVwIGludGFjdCBhbGwgdGhlIG5vdGljZXMgdGhhdCByZWZlciB0byB0aGlzIExpY2Vuc2UgYW5k +IHRvIHRoZSBhYnNlbmNlIG9mIGFueSB3YXJyYW50eTsgYW5kIGdpdmUgYW55IG90aGVyIHJlY2lw +aWVudHMgb2YgdGhlIFByb2dyYW0gYSBjb3B5IG9mIHRoaXMgTGljZW5zZSBhbG9uZyB3aXRoIHRo +ZSBQcm9ncmFtLgoKWW91IG1heSBjaGFyZ2UgYSBmZWUgZm9yIHRoZSBwaHlzaWNhbCBhY3Qgb2Yg +dHJhbnNmZXJyaW5nIGEgY29weSwgYW5kIHlvdSBtYXkgYXQgeW91ciBvcHRpb24gb2ZmZXIgd2Fy +cmFudHkgcHJvdGVjdGlvbiBpbiBleGNoYW5nZSBmb3IgYSBmZWUuCgoyLiBZb3UgbWF5IG1vZGlm +eSB5b3VyIGNvcHkgb3IgY29waWVzIG9mIHRoZSBQcm9ncmFtIG9yIGFueSBwb3J0aW9uIG9mIGl0 +LCB0aHVzIGZvcm1pbmcgYSB3b3JrIGJhc2VkIG9uIHRoZSBQcm9ncmFtLCBhbmQgY29weSBhbmQg +ZGlzdHJpYnV0ZSBzdWNoIG1vZGlmaWNhdGlvbnMgb3Igd29yayB1bmRlciB0aGUgdGVybXMgb2Yg +U2VjdGlvbiAxIGFib3ZlLCBwcm92aWRlZCB0aGF0IHlvdSBhbHNvIG1lZXQgYWxsIG9mIHRoZXNl +IGNvbmRpdGlvbnM6CgogICAgKiBhKSBZb3UgbXVzdCBjYXVzZSB0aGUgbW9kaWZpZWQgZmlsZXMg +dG8gY2FycnkgcHJvbWluZW50IG5vdGljZXMgc3RhdGluZyB0aGF0IHlvdSBjaGFuZ2VkIHRoZSBm +aWxlcyBhbmQgdGhlIGRhdGUgb2YgYW55IGNoYW5nZS4KCiAgICAqIGIpIFlvdSBtdXN0IGNhdXNl +IGFueSB3b3JrIHRoYXQgeW91IGRpc3RyaWJ1dGUgb3IgcHVibGlzaCwgdGhhdCBpbiB3aG9sZSBv +ciBpbiBwYXJ0IGNvbnRhaW5zIG9yIGlzIGRlcml2ZWQgZnJvbSB0aGUgUHJvZ3JhbSBvciBhbnkg +cGFydCB0aGVyZW9mLCB0byBiZSBsaWNlbnNlZCBhcyBhIHdob2xlIGF0IG5vIGNoYXJnZSB0byBh +bGwgdGhpcmQgcGFydGllcyB1bmRlciB0aGUgdGVybXMgb2YgdGhpcyBMaWNlbnNlLgoKICAgICog +YykgSWYgdGhlIG1vZGlmaWVkIHByb2dyYW0gbm9ybWFsbHkgcmVhZHMgY29tbWFuZHMgaW50ZXJh +Y3RpdmVseSB3aGVuIHJ1biwgeW91IG11c3QgY2F1c2UgaXQsIHdoZW4gc3RhcnRlZCBydW5uaW5n +IGZvciBzdWNoIGludGVyYWN0aXZlIHVzZSBpbiB0aGUgbW9zdCBvcmRpbmFyeSB3YXksIHRvIHBy +aW50IG9yIGRpc3BsYXkgYW4gYW5ub3VuY2VtZW50IGluY2x1ZGluZyBhbiBhcHByb3ByaWF0ZSBj +b3B5cmlnaHQgbm90aWNlIGFuZCBhIG5vdGljZSB0aGF0IHRoZXJlIGlzIG5vIHdhcnJhbnR5IChv +ciBlbHNlLCBzYXlpbmcgdGhhdCB5b3UgcHJvdmlkZSBhIHdhcnJhbnR5KSBhbmQgdGhhdCB1c2Vy +cyBtYXkgcmVkaXN0cmlidXRlIHRoZSBwcm9ncmFtIHVuZGVyIHRoZXNlIGNvbmRpdGlvbnMsIGFu +ZCB0ZWxsaW5nIHRoZSB1c2VyIGhvdyB0byB2aWV3IGEgY29weSBvZiB0aGlzIExpY2Vuc2UuIChF +eGNlcHRpb246IGlmIHRoZSBQcm9ncmFtIGl0c2VsZiBpcyBpbnRlcmFjdGl2ZSBidXQgZG9lcyBu +b3Qgbm9ybWFsbHkgcHJpbnQgc3VjaCBhbiBhbm5vdW5jZW1lbnQsIHlvdXIgd29yayBiYXNlZCBv +biB0aGUgUHJvZ3JhbSBpcyBub3QgcmVxdWlyZWQgdG8gcHJpbnQgYW4gYW5ub3VuY2VtZW50Likg +CgpUaGVzZSByZXF1aXJlbWVudHMgYXBwbHkgdG8gdGhlIG1vZGlmaWVkIHdvcmsgYXMgYSB3aG9s +ZS4gSWYgaWRlbnRpZmlhYmxlIHNlY3Rpb25zIG9mIHRoYXQgd29yayBhcmUgbm90IGRlcml2ZWQg +ZnJvbSB0aGUgUHJvZ3JhbSwgYW5kIGNhbiBiZSByZWFzb25hYmx5IGNvbnNpZGVyZWQgaW5kZXBl +bmRlbnQgYW5kIHNlcGFyYXRlIHdvcmtzIGluIHRoZW1zZWx2ZXMsIHRoZW4gdGhpcyBMaWNlbnNl +LCBhbmQgaXRzIHRlcm1zLCBkbyBub3QgYXBwbHkgdG8gdGhvc2Ugc2VjdGlvbnMgd2hlbiB5b3Ug +ZGlzdHJpYnV0ZSB0aGVtIGFzIHNlcGFyYXRlIHdvcmtzLiBCdXQgd2hlbiB5b3UgZGlzdHJpYnV0 +ZSB0aGUgc2FtZSBzZWN0aW9ucyBhcyBwYXJ0IG9mIGEgd2hvbGUgd2hpY2ggaXMgYSB3b3JrIGJh +c2VkIG9uIHRoZSBQcm9ncmFtLCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSB3aG9sZSBtdXN0IGJl +IG9uIHRoZSB0ZXJtcyBvZiB0aGlzIExpY2Vuc2UsIHdob3NlIHBlcm1pc3Npb25zIGZvciBvdGhl +ciBsaWNlbnNlZXMgZXh0ZW5kIHRvIHRoZSBlbnRpcmUgd2hvbGUsIGFuZCB0aHVzIHRvIGVhY2gg +YW5kIGV2ZXJ5IHBhcnQgcmVnYXJkbGVzcyBvZiB3aG8gd3JvdGUgaXQuCgpUaHVzLCBpdCBpcyBu +b3QgdGhlIGludGVudCBvZiB0aGlzIHNlY3Rpb24gdG8gY2xhaW0gcmlnaHRzIG9yIGNvbnRlc3Qg +eW91ciByaWdodHMgdG8gd29yayB3cml0dGVuIGVudGlyZWx5IGJ5IHlvdTsgcmF0aGVyLCB0aGUg +aW50ZW50IGlzIHRvIGV4ZXJjaXNlIHRoZSByaWdodCB0byBjb250cm9sIHRoZSBkaXN0cmlidXRp +b24gb2YgZGVyaXZhdGl2ZSBvciBjb2xsZWN0aXZlIHdvcmtzIGJhc2VkIG9uIHRoZSBQcm9ncmFt +LgoKSW4gYWRkaXRpb24sIG1lcmUgYWdncmVnYXRpb24gb2YgYW5vdGhlciB3b3JrIG5vdCBiYXNl +ZCBvbiB0aGUgUHJvZ3JhbSB3aXRoIHRoZSBQcm9ncmFtIChvciB3aXRoIGEgd29yayBiYXNlZCBv +biB0aGUgUHJvZ3JhbSkgb24gYSB2b2x1bWUgb2YgYSBzdG9yYWdlIG9yIGRpc3RyaWJ1dGlvbiBt +ZWRpdW0gZG9lcyBub3QgYnJpbmcgdGhlIG90aGVyIHdvcmsgdW5kZXIgdGhlIHNjb3BlIG9mIHRo +aXMgTGljZW5zZS4KCjMuIFlvdSBtYXkgY29weSBhbmQgZGlzdHJpYnV0ZSB0aGUgUHJvZ3JhbSAo +b3IgYSB3b3JrIGJhc2VkIG9uIGl0LCB1bmRlciBTZWN0aW9uIDIpIGluIG9iamVjdCBjb2RlIG9y +IGV4ZWN1dGFibGUgZm9ybSB1bmRlciB0aGUgdGVybXMgb2YgU2VjdGlvbnMgMSBhbmQgMiBhYm92 +ZSBwcm92aWRlZCB0aGF0IHlvdSBhbHNvIGRvIG9uZSBvZiB0aGUgZm9sbG93aW5nOgoKICAgICog +YSkgQWNjb21wYW55IGl0IHdpdGggdGhlIGNvbXBsZXRlIGNvcnJlc3BvbmRpbmcgbWFjaGluZS1y +ZWFkYWJsZSBzb3VyY2UgY29kZSwgd2hpY2ggbXVzdCBiZSBkaXN0cmlidXRlZCB1bmRlciB0aGUg +dGVybXMgb2YgU2VjdGlvbnMgMSBhbmQgMiBhYm92ZSBvbiBhIG1lZGl1bSBjdXN0b21hcmlseSB1 +c2VkIGZvciBzb2Z0d2FyZSBpbnRlcmNoYW5nZTsgb3IsCgogICAgKiBiKSBBY2NvbXBhbnkgaXQg +d2l0aCBhIHdyaXR0ZW4gb2ZmZXIsIHZhbGlkIGZvciBhdCBsZWFzdCB0aHJlZSB5ZWFycywgdG8g +Z2l2ZSBhbnkgdGhpcmQgcGFydHksIGZvciBhIGNoYXJnZSBubyBtb3JlIHRoYW4geW91ciBjb3N0 +IG9mIHBoeXNpY2FsbHkgcGVyZm9ybWluZyBzb3VyY2UgZGlzdHJpYnV0aW9uLCBhIGNvbXBsZXRl +IG1hY2hpbmUtcmVhZGFibGUgY29weSBvZiB0aGUgY29ycmVzcG9uZGluZyBzb3VyY2UgY29kZSwg +dG8gYmUgZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIHRlcm1zIG9mIFNlY3Rpb25zIDEgYW5kIDIgYWJv +dmUgb24gYSBtZWRpdW0gY3VzdG9tYXJpbHkgdXNlZCBmb3Igc29mdHdhcmUgaW50ZXJjaGFuZ2U7 +IG9yLAoKICAgICogYykgQWNjb21wYW55IGl0IHdpdGggdGhlIGluZm9ybWF0aW9uIHlvdSByZWNl +aXZlZCBhcyB0byB0aGUgb2ZmZXIgdG8gZGlzdHJpYnV0ZSBjb3JyZXNwb25kaW5nIHNvdXJjZSBj +b2RlLiAoVGhpcyBhbHRlcm5hdGl2ZSBpcyBhbGxvd2VkIG9ubHkgZm9yIG5vbmNvbW1lcmNpYWwg +ZGlzdHJpYnV0aW9uIGFuZCBvbmx5IGlmIHlvdSByZWNlaXZlZCB0aGUgcHJvZ3JhbSBpbiBvYmpl +Y3QgY29kZSBvciBleGVjdXRhYmxlIGZvcm0gd2l0aCBzdWNoIGFuIG9mZmVyLCBpbiBhY2NvcmQg +d2l0aCBTdWJzZWN0aW9uIGIgYWJvdmUuKSAKClRoZSBzb3VyY2UgY29kZSBmb3IgYSB3b3JrIG1l +YW5zIHRoZSBwcmVmZXJyZWQgZm9ybSBvZiB0aGUgd29yayBmb3IgbWFraW5nIG1vZGlmaWNhdGlv +bnMgdG8gaXQuIEZvciBhbiBleGVjdXRhYmxlIHdvcmssIGNvbXBsZXRlIHNvdXJjZSBjb2RlIG1l +YW5zIGFsbCB0aGUgc291cmNlIGNvZGUgZm9yIGFsbCBtb2R1bGVzIGl0IGNvbnRhaW5zLCBwbHVz +IGFueSBhc3NvY2lhdGVkIGludGVyZmFjZSBkZWZpbml0aW9uIGZpbGVzLCBwbHVzIHRoZSBzY3Jp +cHRzIHVzZWQgdG8gY29udHJvbCBjb21waWxhdGlvbiBhbmQgaW5zdGFsbGF0aW9uIG9mIHRoZSBl +eGVjdXRhYmxlLiBIb3dldmVyLCBhcyBhIHNwZWNpYWwgZXhjZXB0aW9uLCB0aGUgc291cmNlIGNv +ZGUgZGlzdHJpYnV0ZWQgbmVlZCBub3QgaW5jbHVkZSBhbnl0aGluZyB0aGF0IGlzIG5vcm1hbGx5 +IGRpc3RyaWJ1dGVkIChpbiBlaXRoZXIgc291cmNlIG9yIGJpbmFyeSBmb3JtKSB3aXRoIHRoZSBt +YWpvciBjb21wb25lbnRzIChjb21waWxlciwga2VybmVsLCBhbmQgc28gb24pIG9mIHRoZSBvcGVy +YXRpbmcgc3lzdGVtIG9uIHdoaWNoIHRoZSBleGVjdXRhYmxlIHJ1bnMsIHVubGVzcyB0aGF0IGNv +bXBvbmVudCBpdHNlbGYgYWNjb21wYW5pZXMgdGhlIGV4ZWN1dGFibGUuCgpJZiBkaXN0cmlidXRp +b24gb2YgZXhlY3V0YWJsZSBvciBvYmplY3QgY29kZSBpcyBtYWRlIGJ5IG9mZmVyaW5nIGFjY2Vz +cyB0byBjb3B5IGZyb20gYSBkZXNpZ25hdGVkIHBsYWNlLCB0aGVuIG9mZmVyaW5nIGVxdWl2YWxl +bnQgYWNjZXNzIHRvIGNvcHkgdGhlIHNvdXJjZSBjb2RlIGZyb20gdGhlIHNhbWUgcGxhY2UgY291 +bnRzIGFzIGRpc3RyaWJ1dGlvbiBvZiB0aGUgc291cmNlIGNvZGUsIGV2ZW4gdGhvdWdoIHRoaXJk +IHBhcnRpZXMgYXJlIG5vdCBjb21wZWxsZWQgdG8gY29weSB0aGUgc291cmNlIGFsb25nIHdpdGgg +dGhlIG9iamVjdCBjb2RlLgoKNC4gWW91IG1heSBub3QgY29weSwgbW9kaWZ5LCBzdWJsaWNlbnNl +LCBvciBkaXN0cmlidXRlIHRoZSBQcm9ncmFtIGV4Y2VwdCBhcyBleHByZXNzbHkgcHJvdmlkZWQg +dW5kZXIgdGhpcyBMaWNlbnNlLiBBbnkgYXR0ZW1wdCBvdGhlcndpc2UgdG8gY29weSwgbW9kaWZ5 +LCBzdWJsaWNlbnNlIG9yIGRpc3RyaWJ1dGUgdGhlIFByb2dyYW0gaXMgdm9pZCwgYW5kIHdpbGwg +YXV0b21hdGljYWxseSB0ZXJtaW5hdGUgeW91ciByaWdodHMgdW5kZXIgdGhpcyBMaWNlbnNlLiBI +b3dldmVyLCBwYXJ0aWVzIHdobyBoYXZlIHJlY2VpdmVkIGNvcGllcywgb3IgcmlnaHRzLCBmcm9t +IHlvdSB1bmRlciB0aGlzIExpY2Vuc2Ugd2lsbCBub3QgaGF2ZSB0aGVpciBsaWNlbnNlcyB0ZXJt +aW5hdGVkIHNvIGxvbmcgYXMgc3VjaCBwYXJ0aWVzIHJlbWFpbiBpbiBmdWxsIGNvbXBsaWFuY2Uu +Cgo1LiBZb3UgYXJlIG5vdCByZXF1aXJlZCB0byBhY2NlcHQgdGhpcyBMaWNlbnNlLCBzaW5jZSB5 +b3UgaGF2ZSBub3Qgc2lnbmVkIGl0LiBIb3dldmVyLCBub3RoaW5nIGVsc2UgZ3JhbnRzIHlvdSBw +ZXJtaXNzaW9uIHRvIG1vZGlmeSBvciBkaXN0cmlidXRlIHRoZSBQcm9ncmFtIG9yIGl0cyBkZXJp +dmF0aXZlIHdvcmtzLiBUaGVzZSBhY3Rpb25zIGFyZSBwcm9oaWJpdGVkIGJ5IGxhdyBpZiB5b3Ug +ZG8gbm90IGFjY2VwdCB0aGlzIExpY2Vuc2UuIFRoZXJlZm9yZSwgYnkgbW9kaWZ5aW5nIG9yIGRp +c3RyaWJ1dGluZyB0aGUgUHJvZ3JhbSAob3IgYW55IHdvcmsgYmFzZWQgb24gdGhlIFByb2dyYW0p +LCB5b3UgaW5kaWNhdGUgeW91ciBhY2NlcHRhbmNlIG9mIHRoaXMgTGljZW5zZSB0byBkbyBzbywg +YW5kIGFsbCBpdHMgdGVybXMgYW5kIGNvbmRpdGlvbnMgZm9yIGNvcHlpbmcsIGRpc3RyaWJ1dGlu +ZyBvciBtb2RpZnlpbmcgdGhlIFByb2dyYW0gb3Igd29ya3MgYmFzZWQgb24gaXQuCgo2LiBFYWNo +IHRpbWUgeW91IHJlZGlzdHJpYnV0ZSB0aGUgUHJvZ3JhbSAob3IgYW55IHdvcmsgYmFzZWQgb24g +dGhlIFByb2dyYW0pLCB0aGUgcmVjaXBpZW50IGF1dG9tYXRpY2FsbHkgcmVjZWl2ZXMgYSBsaWNl +bnNlIGZyb20gdGhlIG9yaWdpbmFsIGxpY2Vuc29yIHRvIGNvcHksIGRpc3RyaWJ1dGUgb3IgbW9k +aWZ5IHRoZSBQcm9ncmFtIHN1YmplY3QgdG8gdGhlc2UgdGVybXMgYW5kIGNvbmRpdGlvbnMuIFlv +dSBtYXkgbm90IGltcG9zZSBhbnkgZnVydGhlciByZXN0cmljdGlvbnMgb24gdGhlIHJlY2lwaWVu +dHMnIGV4ZXJjaXNlIG9mIHRoZSByaWdodHMgZ3JhbnRlZCBoZXJlaW4uIFlvdSBhcmUgbm90IHJl +c3BvbnNpYmxlIGZvciBlbmZvcmNpbmcgY29tcGxpYW5jZSBieSB0aGlyZCBwYXJ0aWVzIHRvIHRo +aXMgTGljZW5zZS4KCjcuIElmLCBhcyBhIGNvbnNlcXVlbmNlIG9mIGEgY291cnQganVkZ21lbnQg +b3IgYWxsZWdhdGlvbiBvZiBwYXRlbnQgaW5mcmluZ2VtZW50IG9yIGZvciBhbnkgb3RoZXIgcmVh +c29uIChub3QgbGltaXRlZCB0byBwYXRlbnQgaXNzdWVzKSwgY29uZGl0aW9ucyBhcmUgaW1wb3Nl +ZCBvbiB5b3UgKHdoZXRoZXIgYnkgY291cnQgb3JkZXIsIGFncmVlbWVudCBvciBvdGhlcndpc2Up +IHRoYXQgY29udHJhZGljdCB0aGUgY29uZGl0aW9ucyBvZiB0aGlzIExpY2Vuc2UsIHRoZXkgZG8g +bm90IGV4Y3VzZSB5b3UgZnJvbSB0aGUgY29uZGl0aW9ucyBvZiB0aGlzIExpY2Vuc2UuIElmIHlv +dSBjYW5ub3QgZGlzdHJpYnV0ZSBzbyBhcyB0byBzYXRpc2Z5IHNpbXVsdGFuZW91c2x5IHlvdXIg +b2JsaWdhdGlvbnMgdW5kZXIgdGhpcyBMaWNlbnNlIGFuZCBhbnkgb3RoZXIgcGVydGluZW50IG9i +bGlnYXRpb25zLCB0aGVuIGFzIGEgY29uc2VxdWVuY2UgeW91IG1heSBub3QgZGlzdHJpYnV0ZSB0 +aGUgUHJvZ3JhbSBhdCBhbGwuIEZvciBleGFtcGxlLCBpZiBhIHBhdGVudCBsaWNlbnNlIHdvdWxk +IG5vdCBwZXJtaXQgcm95YWx0eS1mcmVlIHJlZGlzdHJpYnV0aW9uIG9mIHRoZSBQcm9ncmFtIGJ5 +IGFsbCB0aG9zZSB3aG8gcmVjZWl2ZSBjb3BpZXMgZGlyZWN0bHkgb3IgaW5kaXJlY3RseSB0aHJv +dWdoIHlvdSwgdGhlbiB0aGUgb25seSB3YXkgeW91IGNvdWxkIHNhdGlzZnkgYm90aCBpdCBhbmQg +dGhpcyBMaWNlbnNlIHdvdWxkIGJlIHRvIHJlZnJhaW4gZW50aXJlbHkgZnJvbSBkaXN0cmlidXRp +b24gb2YgdGhlIFByb2dyYW0uCgpJZiBhbnkgcG9ydGlvbiBvZiB0aGlzIHNlY3Rpb24gaXMgaGVs +ZCBpbnZhbGlkIG9yIHVuZW5mb3JjZWFibGUgdW5kZXIgYW55IHBhcnRpY3VsYXIgY2lyY3Vtc3Rh +bmNlLCB0aGUgYmFsYW5jZSBvZiB0aGUgc2VjdGlvbiBpcyBpbnRlbmRlZCB0byBhcHBseSBhbmQg +dGhlIHNlY3Rpb24gYXMgYSB3aG9sZSBpcyBpbnRlbmRlZCB0byBhcHBseSBpbiBvdGhlciBjaXJj +dW1zdGFuY2VzLgoKSXQgaXMgbm90IHRoZSBwdXJwb3NlIG9mIHRoaXMgc2VjdGlvbiB0byBpbmR1 +Y2UgeW91IHRvIGluZnJpbmdlIGFueSBwYXRlbnRzIG9yIG90aGVyIHByb3BlcnR5IHJpZ2h0IGNs +YWltcyBvciB0byBjb250ZXN0IHZhbGlkaXR5IG9mIGFueSBzdWNoIGNsYWltczsgdGhpcyBzZWN0 +aW9uIGhhcyB0aGUgc29sZSBwdXJwb3NlIG9mIHByb3RlY3RpbmcgdGhlIGludGVncml0eSBvZiB0 +aGUgZnJlZSBzb2Z0d2FyZSBkaXN0cmlidXRpb24gc3lzdGVtLCB3aGljaCBpcyBpbXBsZW1lbnRl +ZCBieSBwdWJsaWMgbGljZW5zZSBwcmFjdGljZXMuIE1hbnkgcGVvcGxlIGhhdmUgbWFkZSBnZW5l +cm91cyBjb250cmlidXRpb25zIHRvIHRoZSB3aWRlIHJhbmdlIG9mIHNvZnR3YXJlIGRpc3RyaWJ1 +dGVkIHRocm91Z2ggdGhhdCBzeXN0ZW0gaW4gcmVsaWFuY2Ugb24gY29uc2lzdGVudCBhcHBsaWNh +dGlvbiBvZiB0aGF0IHN5c3RlbTsgaXQgaXMgdXAgdG8gdGhlIGF1dGhvci9kb25vciB0byBkZWNp +ZGUgaWYgaGUgb3Igc2hlIGlzIHdpbGxpbmcgdG8gZGlzdHJpYnV0ZSBzb2Z0d2FyZSB0aHJvdWdo +IGFueSBvdGhlciBzeXN0ZW0gYW5kIGEgbGljZW5zZWUgY2Fubm90IGltcG9zZSB0aGF0IGNob2lj +ZS4KClRoaXMgc2VjdGlvbiBpcyBpbnRlbmRlZCB0byBtYWtlIHRob3JvdWdobHkgY2xlYXIgd2hh +dCBpcyBiZWxpZXZlZCB0byBiZSBhIGNvbnNlcXVlbmNlIG9mIHRoZSByZXN0IG9mIHRoaXMgTGlj +ZW5zZS4KCjguIElmIHRoZSBkaXN0cmlidXRpb24gYW5kL29yIHVzZSBvZiB0aGUgUHJvZ3JhbSBp +cyByZXN0cmljdGVkIGluIGNlcnRhaW4gY291bnRyaWVzIGVpdGhlciBieSBwYXRlbnRzIG9yIGJ5 +IGNvcHlyaWdodGVkIGludGVyZmFjZXMsIHRoZSBvcmlnaW5hbCBjb3B5cmlnaHQgaG9sZGVyIHdo +byBwbGFjZXMgdGhlIFByb2dyYW0gdW5kZXIgdGhpcyBMaWNlbnNlIG1heSBhZGQgYW4gZXhwbGlj +aXQgZ2VvZ3JhcGhpY2FsIGRpc3RyaWJ1dGlvbiBsaW1pdGF0aW9uIGV4Y2x1ZGluZyB0aG9zZSBj +b3VudHJpZXMsIHNvIHRoYXQgZGlzdHJpYnV0aW9uIGlzIHBlcm1pdHRlZCBvbmx5IGluIG9yIGFt +b25nIGNvdW50cmllcyBub3QgdGh1cyBleGNsdWRlZC4gSW4gc3VjaCBjYXNlLCB0aGlzIExpY2Vu +c2UgaW5jb3Jwb3JhdGVzIHRoZSBsaW1pdGF0aW9uIGFzIGlmIHdyaXR0ZW4gaW4gdGhlIGJvZHkg +b2YgdGhpcyBMaWNlbnNlLgoKOS4gVGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiBtYXkgcHVi +bGlzaCByZXZpc2VkIGFuZC9vciBuZXcgdmVyc2lvbnMgb2YgdGhlIEdlbmVyYWwgUHVibGljIExp +Y2Vuc2UgZnJvbSB0aW1lIHRvIHRpbWUuIFN1Y2ggbmV3IHZlcnNpb25zIHdpbGwgYmUgc2ltaWxh +ciBpbiBzcGlyaXQgdG8gdGhlIHByZXNlbnQgdmVyc2lvbiwgYnV0IG1heSBkaWZmZXIgaW4gZGV0 +YWlsIHRvIGFkZHJlc3MgbmV3IHByb2JsZW1zIG9yIGNvbmNlcm5zLgoKRWFjaCB2ZXJzaW9uIGlz +IGdpdmVuIGEgZGlzdGluZ3Vpc2hpbmcgdmVyc2lvbiBudW1iZXIuIElmIHRoZSBQcm9ncmFtIHNw +ZWNpZmllcyBhIHZlcnNpb24gbnVtYmVyIG9mIHRoaXMgTGljZW5zZSB3aGljaCBhcHBsaWVzIHRv +IGl0IGFuZCAiYW55IGxhdGVyIHZlcnNpb24iLCB5b3UgaGF2ZSB0aGUgb3B0aW9uIG9mIGZvbGxv +d2luZyB0aGUgdGVybXMgYW5kIGNvbmRpdGlvbnMgZWl0aGVyIG9mIHRoYXQgdmVyc2lvbiBvciBv +ZiBhbnkgbGF0ZXIgdmVyc2lvbiBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRh +dGlvbi4gSWYgdGhlIFByb2dyYW0gZG9lcyBub3Qgc3BlY2lmeSBhIHZlcnNpb24gbnVtYmVyIG9m +IHRoaXMgTGljZW5zZSwgeW91IG1heSBjaG9vc2UgYW55IHZlcnNpb24gZXZlciBwdWJsaXNoZWQg +YnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbi4KCjEwLiBJZiB5b3Ugd2lzaCB0byBpbmNv +cnBvcmF0ZSBwYXJ0cyBvZiB0aGUgUHJvZ3JhbSBpbnRvIG90aGVyIGZyZWUgcHJvZ3JhbXMgd2hv +c2UgZGlzdHJpYnV0aW9uIGNvbmRpdGlvbnMgYXJlIGRpZmZlcmVudCwgd3JpdGUgdG8gdGhlIGF1 +dGhvciB0byBhc2sgZm9yIHBlcm1pc3Npb24uIEZvciBzb2Z0d2FyZSB3aGljaCBpcyBjb3B5cmln +aHRlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCB3cml0ZSB0byB0aGUgRnJlZSBT +b2Z0d2FyZSBGb3VuZGF0aW9uOyB3ZSBzb21ldGltZXMgbWFrZSBleGNlcHRpb25zIGZvciB0aGlz +LiBPdXIgZGVjaXNpb24gd2lsbCBiZSBndWlkZWQgYnkgdGhlIHR3byBnb2FscyBvZiBwcmVzZXJ2 +aW5nIHRoZSBmcmVlIHN0YXR1cyBvZiBhbGwgZGVyaXZhdGl2ZXMgb2Ygb3VyIGZyZWUgc29mdHdh +cmUgYW5kIG9mIHByb21vdGluZyB0aGUgc2hhcmluZyBhbmQgcmV1c2Ugb2Ygc29mdHdhcmUgZ2Vu +ZXJhbGx5LgoKTk8gV0FSUkFOVFkKCjExLiBCRUNBVVNFIFRIRSBQUk9HUkFNIElTIExJQ0VOU0VE +IEZSRUUgT0YgQ0hBUkdFLCBUSEVSRSBJUyBOTyBXQVJSQU5UWSBGT1IgVEhFIFBST0dSQU0sIFRP +IFRIRSBFWFRFTlQgUEVSTUlUVEVEIEJZIEFQUExJQ0FCTEUgTEFXLiBFWENFUFQgV0hFTiBPVEhF +UldJU0UgU1RBVEVEIElOIFdSSVRJTkcgVEhFIENPUFlSSUdIVCBIT0xERVJTIEFORC9PUiBPVEhF +UiBQQVJUSUVTIFBST1ZJREUgVEhFIFBST0dSQU0gIkFTIElTIiBXSVRIT1VUIFdBUlJBTlRZIE9G +IEFOWSBLSU5ELCBFSVRIRVIgRVhQUkVTU0VEIE9SIElNUExJRUQsIElOQ0xVRElORywgQlVUIE5P +VCBMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBB +TkQgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuIFRIRSBFTlRJUkUgUklTSyBBUyBU +TyBUSEUgUVVBTElUWSBBTkQgUEVSRk9STUFOQ0UgT0YgVEhFIFBST0dSQU0gSVMgV0lUSCBZT1Uu +IFNIT1VMRCBUSEUgUFJPR1JBTSBQUk9WRSBERUZFQ1RJVkUsIFlPVSBBU1NVTUUgVEhFIENPU1Qg +T0YgQUxMIE5FQ0VTU0FSWSBTRVJWSUNJTkcsIFJFUEFJUiBPUiBDT1JSRUNUSU9OLgoKMTIuIElO +IE5PIEVWRU5UIFVOTEVTUyBSRVFVSVJFRCBCWSBBUFBMSUNBQkxFIExBVyBPUiBBR1JFRUQgVE8g +SU4gV1JJVElORyBXSUxMIEFOWSBDT1BZUklHSFQgSE9MREVSLCBPUiBBTlkgT1RIRVIgUEFSVFkg +V0hPIE1BWSBNT0RJRlkgQU5EL09SIFJFRElTVFJJQlVURSBUSEUgUFJPR1JBTSBBUyBQRVJNSVRU +RUQgQUJPVkUsIEJFIExJQUJMRSBUTyBZT1UgRk9SIERBTUFHRVMsIElOQ0xVRElORyBBTlkgR0VO +RVJBTCwgU1BFQ0lBTCwgSU5DSURFTlRBTCBPUiBDT05TRVFVRU5USUFMIERBTUFHRVMgQVJJU0lO +RyBPVVQgT0YgVEhFIFVTRSBPUiBJTkFCSUxJVFkgVE8gVVNFIFRIRSBQUk9HUkFNIChJTkNMVURJ +TkcgQlVUIE5PVCBMSU1JVEVEIFRPIExPU1MgT0YgREFUQSBPUiBEQVRBIEJFSU5HIFJFTkRFUkVE +IElOQUNDVVJBVEUgT1IgTE9TU0VTIFNVU1RBSU5FRCBCWSBZT1UgT1IgVEhJUkQgUEFSVElFUyBP +UiBBIEZBSUxVUkUgT0YgVEhFIFBST0dSQU0gVE8gT1BFUkFURSBXSVRIIEFOWSBPVEhFUiBQUk9H +UkFNUyksIEVWRU4gSUYgU1VDSCBIT0xERVIgT1IgT1RIRVIgUEFSVFkgSEFTIEJFRU4gQURWSVNF +RCBPRiBUSEUgUE9TU0lCSUxJVFkgT0YgU1VDSCBEQU1BR0VTLgoKRU5EIE9GIFRFUk1TIEFORCBD +T05ESVRJT05TCgpIb3cgdG8gQXBwbHkgVGhlc2UgVGVybXMgdG8gWW91ciBOZXcgUHJvZ3JhbXMK +CklmIHlvdSBkZXZlbG9wIGEgbmV3IHByb2dyYW0sIGFuZCB5b3Ugd2FudCBpdCB0byBiZSBvZiB0 +aGUgZ3JlYXRlc3QgcG9zc2libGUgdXNlIHRvIHRoZSBwdWJsaWMsIHRoZSBiZXN0IHdheSB0byBh +Y2hpZXZlIHRoaXMgaXMgdG8gbWFrZSBpdCBmcmVlIHNvZnR3YXJlIHdoaWNoIGV2ZXJ5b25lIGNh +biByZWRpc3RyaWJ1dGUgYW5kIGNoYW5nZSB1bmRlciB0aGVzZSB0ZXJtcy4KClRvIGRvIHNvLCBh +dHRhY2ggdGhlIGZvbGxvd2luZyBub3RpY2VzIHRvIHRoZSBwcm9ncmFtLiBJdCBpcyBzYWZlc3Qg +dG8gYXR0YWNoIHRoZW0gdG8gdGhlIHN0YXJ0IG9mIGVhY2ggc291cmNlIGZpbGUgdG8gbW9zdCBl +ZmZlY3RpdmVseSBjb252ZXkgdGhlIGV4Y2x1c2lvbiBvZiB3YXJyYW50eTsgYW5kIGVhY2ggZmls +ZSBzaG91bGQgaGF2ZSBhdCBsZWFzdCB0aGUgImNvcHlyaWdodCIgbGluZSBhbmQgYSBwb2ludGVy +IHRvIHdoZXJlIHRoZSBmdWxsIG5vdGljZSBpcyBmb3VuZC4KCglvbmUgbGluZSB0byBnaXZlIHRo +ZSBwcm9ncmFtJ3MgbmFtZSBhbmQgYW4gaWRlYSBvZiB3aGF0IGl0IGRvZXMuCglDb3B5cmlnaHQg +KEMpIHl5eXkgIG5hbWUgb2YgYXV0aG9yCgoJVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7 +IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vcgoJbW9kaWZ5IGl0IHVuZGVyIHRoZSB0ZXJt +cyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UKCWFzIHB1Ymxpc2hlZCBieSB0aGUg +RnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyBlaXRoZXIgdmVyc2lvbiAyCglvZiB0aGUgTGljZW5z +ZSwgb3IgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi4KCglUaGlzIHByb2dyYW0g +aXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwKCWJ1dCBX +SVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9m +CglNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBT +ZWUgdGhlCglHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgoKCVlv +dSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBM +aWNlbnNlCglhbG9uZyB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJl +ZSBTb2Z0d2FyZQoJRm91bmRhdGlvbiwgSW5jLiwgNTkgVGVtcGxlIFBsYWNlIC0gU3VpdGUgMzMw +LCBCb3N0b24sIE1BICAwMjExMS0xMzA3LCBVU0EuCgpBbHNvIGFkZCBpbmZvcm1hdGlvbiBvbiBo +b3cgdG8gY29udGFjdCB5b3UgYnkgZWxlY3Ryb25pYyBhbmQgcGFwZXIgbWFpbC4KCklmIHRoZSBw +cm9ncmFtIGlzIGludGVyYWN0aXZlLCBtYWtlIGl0IG91dHB1dCBhIHNob3J0IG5vdGljZSBsaWtl +IHRoaXMgd2hlbiBpdCBzdGFydHMgaW4gYW4gaW50ZXJhY3RpdmUgbW9kZToKCglHbm9tb3Zpc2lv +biB2ZXJzaW9uIDY5LCBDb3B5cmlnaHQgKEMpIHllYXIgbmFtZSBvZiBhdXRob3IKCUdub21vdmlz +aW9uIGNvbWVzIHdpdGggQUJTT0xVVEVMWSBOTyBXQVJSQU5UWTsgZm9yIGRldGFpbHMKCXR5cGUg +YHNob3cgdycuICBUaGlzIGlzIGZyZWUgc29mdHdhcmUsIGFuZCB5b3UgYXJlIHdlbGNvbWUKCXRv +IHJlZGlzdHJpYnV0ZSBpdCB1bmRlciBjZXJ0YWluIGNvbmRpdGlvbnM7IHR5cGUgYHNob3cgYycg +Cglmb3IgZGV0YWlscy4KClRoZSBoeXBvdGhldGljYWwgY29tbWFuZHMgYHNob3cgdycgYW5kIGBz +aG93IGMnIHNob3VsZCBzaG93IHRoZSBhcHByb3ByaWF0ZSBwYXJ0cyBvZiB0aGUgR2VuZXJhbCBQ +dWJsaWMgTGljZW5zZS4gT2YgY291cnNlLCB0aGUgY29tbWFuZHMgeW91IHVzZSBtYXkgYmUgY2Fs +bGVkIHNvbWV0aGluZyBvdGhlciB0aGFuIGBzaG93IHcnIGFuZCBgc2hvdyBjJzsgdGhleSBjb3Vs +ZCBldmVuIGJlIG1vdXNlLWNsaWNrcyBvciBtZW51IGl0ZW1zLS13aGF0ZXZlciBzdWl0cyB5b3Vy +IHByb2dyYW0uCgpZb3Ugc2hvdWxkIGFsc28gZ2V0IHlvdXIgZW1wbG95ZXIgKGlmIHlvdSB3b3Jr +IGFzIGEgcHJvZ3JhbW1lcikgb3IgeW91ciBzY2hvb2wsIGlmIGFueSwgdG8gc2lnbiBhICJjb3B5 +cmlnaHQgZGlzY2xhaW1lciIgZm9yIHRoZSBwcm9ncmFtLCBpZiBuZWNlc3NhcnkuIEhlcmUgaXMg +YSBzYW1wbGU7IGFsdGVyIHRoZSBuYW1lczoKCglZb3lvZHluZSwgSW5jLiwgaGVyZWJ5IGRpc2Ns +YWltcyBhbGwgY29weXJpZ2h0CglpbnRlcmVzdCBpbiB0aGUgcHJvZ3JhbSBgR25vbW92aXNpb24n +Cgkod2hpY2ggbWFrZXMgcGFzc2VzIGF0IGNvbXBpbGVycykgd3JpdHRlbiAKCWJ5IEphbWVzIEhh +Y2tlci4KCglzaWduYXR1cmUgb2YgVHkgQ29vbiwgMSBBcHJpbCAxOTg5CglUeSBDb29uLCBQcmVz +aWRlbnQgb2YgVmljZQoKVGhpcyBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGRvZXMgbm90IHBlcm1p +dCBpbmNvcnBvcmF0aW5nIHlvdXIgcHJvZ3JhbSBpbnRvIHByb3ByaWV0YXJ5IHByb2dyYW1zLiBJ +ZiB5b3VyIHByb2dyYW0gaXMgYSBzdWJyb3V0aW5lIGxpYnJhcnksIHlvdSBtYXkgY29uc2lkZXIg +aXQgbW9yZSB1c2VmdWwgdG8gcGVybWl0IGxpbmtpbmcgcHJvcHJpZXRhcnkgYXBwbGljYXRpb25z +IHdpdGggdGhlIGxpYnJhcnkuIElmIHRoaXMgaXMgd2hhdCB5b3Ugd2FudCB0byBkbywgdXNlIHRo +ZSBHTlUgTGVzc2VyIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgaW5zdGVhZCBvZiB0aGlzIExpY2Vu +c2UuCg== \ No newline at end of file diff --git a/tests/encoding/test-suites/encode/gpl.quoted-printable b/tests/encoding/test-suites/encode/gpl.quoted-printable new file mode 100644 index 00000000..c0bacd22 --- /dev/null +++ b/tests/encoding/test-suites/encode/gpl.quoted-printable @@ -0,0 +1,247 @@ +GNU GENERAL PUBLIC LICENSE=0A=0AVersion 2, June 1991=0A=0ACopyright (C) 1= +989, 1991 Free Software Foundation, Inc. =20=0A59 Temple Place - Suite 33= +0, Boston, MA 02111-1307, USA=0A=0AEveryone is permitted to copy and dis= +tribute verbatim copies=0Aof this license document, but changing it is no= +t allowed.=0A=0APreamble=0A=0AThe licenses for most software are designed= + to take away your freedom to share and change it. By contrast, the GNU G= +eneral Public License is intended to guarantee your freedom to share and = +change free software--to make sure the software is free for all its users= +=2E This General Public License applies to most of the Free Software Foun= +dation's software and to any other program whose authors commit to using = +it. (Some other Free Software Foundation software is covered by the GNU L= +ibrary General Public License instead.) You can apply it to your programs= +, too.=0A=0AWhen we speak of free software, we are referring to freedom, = +not price. Our General Public Licenses are designed to make sure that you= + have the freedom to distribute copies of free software (and charge for t= +his service if you wish), that you receive source code or can get it if y= +ou want it, that you can change the software or use pieces of it in new f= +ree programs; and that you know you can do these things.=0A=0ATo protect = +your rights, we need to make restrictions that forbid anyone to deny you = +these rights or to ask you to surrender the rights. These restrictions tr= +anslate to certain responsibilities for you if you distribute copies of t= +he software, or if you modify it.=0A=0AFor example, if you distribute cop= +ies of such a program, whether gratis or for a fee, you must give the rec= +ipients all the rights that you have. You must make sure that they, too, = +receive or can get the source code. And you must show them these terms so= + they know their rights.=0A=0AWe protect your rights with two steps: (1) = +copyright the software, and (2) offer you this license which gives you le= +gal permission to copy, distribute and/or modify the software.=0A=0AAlso,= + for each author's protection and ours, we want to make certain that ever= +yone understands that there is no warranty for this free software. If the= + software is modified by someone else and passed on, we want its recipien= +ts to know that what they have is not the original, so that any problems = +introduced by others will not reflect on the original authors' reputation= +s.=0A=0AFinally, any free program is threatened constantly by software pa= +tents. We wish to avoid the danger that redistributors of a free program = +will individually obtain patent licenses, in effect making the program pr= +oprietary. To prevent this, we have made it clear that any patent must be= + licensed for everyone's free use or not licensed at all.=0A=0AThe precis= +e terms and conditions for copying, distribution and modification follow.= +=0A=0ATERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION=0A=0A= +0. This License applies to any program or other work which contains a not= +ice placed by the copyright holder saying it may be distributed under the= + terms of this General Public License. The "Program", below, refers to an= +y such program or work, and a "work based on the Program" means either th= +e Program or any derivative work under copyright law: that is to say, a w= +ork containing the Program or a portion of it, either verbatim or with mo= +difications and/or translated into another language. (Hereinafter, transl= +ation is included without limitation in the term "modification".) Each li= +censee is addressed as "you".=0A=0AActivities other than copying, distrib= +ution and modification are not covered by this License; they are outside = +its scope. The act of running the Program is not restricted, and the outp= +ut from the Program is covered only if its contents constitute a work bas= +ed on the Program (independent of having been made by running the Program= +). Whether that is true depends on what the Program does.=0A=0A1. You may= + copy and distribute verbatim copies of the Program's source code as you = +receive it, in any medium, provided that you conspicuously and appropriat= +ely publish on each copy an appropriate copyright notice and disclaimer o= +f warranty; keep intact all the notices that refer to this License and to= + the absence of any warranty; and give any other recipients of the Progra= +m a copy of this License along with the Program.=0A=0AYou may charge a fe= +e for the physical act of transferring a copy, and you may at your option= + offer warranty protection in exchange for a fee.=0A=0A2. You may modify = +your copy or copies of the Program or any portion of it, thus forming a w= +ork based on the Program, and copy and distribute such modifications or w= +ork under the terms of Section 1 above, provided that you also meet all o= +f these conditions:=0A=0A * a) You must cause the modified files to ca= +rry prominent notices stating that you changed the files and the date of = +any change.=0A=0A * b) You must cause any work that you distribute or = +publish, that in whole or in part contains or is derived from the Program= + or any part thereof, to be licensed as a whole at no charge to all third= + parties under the terms of this License.=0A=0A * c) If the modified p= +rogram normally reads commands interactively when run, you must cause it,= + when started running for such interactive use in the most ordinary way, = +to print or display an announcement including an appropriate copyright no= +tice and a notice that there is no warranty (or else, saying that you pro= +vide a warranty) and that users may redistribute the program under these = +conditions, and telling the user how to view a copy of this License. (Exc= +eption: if the Program itself is interactive but does not normally print = +such an announcement, your work based on the Program is not required to p= +rint an announcement.)=20=0A=0AThese requirements apply to the modified w= +ork as a whole. If identifiable sections of that work are not derived fro= +m the Program, and can be reasonably considered independent and separate = +works in themselves, then this License, and its terms, do not apply to th= +ose sections when you distribute them as separate works. But when you dis= +tribute the same sections as part of a whole which is a work based on the= + Program, the distribution of the whole must be on the terms of this Lice= +nse, whose permissions for other licensees extend to the entire whole, an= +d thus to each and every part regardless of who wrote it.=0A=0AThus, it i= +s not the intent of this section to claim rights or contest your rights t= +o work written entirely by you; rather, the intent is to exercise the rig= +ht to control the distribution of derivative or collective works based on= + the Program.=0A=0AIn addition, mere aggregation of another work not base= +d on the Program with the Program (or with a work based on the Program) o= +n a volume of a storage or distribution medium does not bring the other w= +ork under the scope of this License.=0A=0A3. You may copy and distribute = +the Program (or a work based on it, under Section 2) in object code or ex= +ecutable form under the terms of Sections 1 and 2 above provided that you= + also do one of the following:=0A=0A * a) Accompany it with the comple= +te corresponding machine-readable source code, which must be distributed = +under the terms of Sections 1 and 2 above on a medium customarily used fo= +r software interchange; or,=0A=0A * b) Accompany it with a written off= +er, valid for at least three years, to give any third party, for a charge= + no more than your cost of physically performing source distribution, a c= +omplete machine-readable copy of the corresponding source code, to be dis= +tributed under the terms of Sections 1 and 2 above on a medium customaril= +y used for software interchange; or,=0A=0A * c) Accompany it with the = +information you received as to the offer to distribute corresponding sour= +ce code. (This alternative is allowed only for noncommercial distribution= + and only if you received the program in object code or executable form w= +ith such an offer, in accord with Subsection b above.)=20=0A=0AThe source= + code for a work means the preferred form of the work for making modifica= +tions to it. For an executable work, complete source code means all the s= +ource code for all modules it contains, plus any associated interface def= +inition files, plus the scripts used to control compilation and installat= +ion of the executable. However, as a special exception, the source code d= +istributed need not include anything that is normally distributed (in eit= +her source or binary form) with the major components (compiler, kernel, a= +nd so on) of the operating system on which the executable runs, unless th= +at component itself accompanies the executable.=0A=0AIf distribution of e= +xecutable or object code is made by offering access to copy from a design= +ated place, then offering equivalent access to copy the source code from = +the same place counts as distribution of the source code, even though thi= +rd parties are not compelled to copy the source along with the object cod= +e.=0A=0A4. You may not copy, modify, sublicense, or distribute the Progra= +m except as expressly provided under this License. Any attempt otherwise = +to copy, modify, sublicense or distribute the Program is void, and will a= +utomatically terminate your rights under this License. However, parties w= +ho have received copies, or rights, from you under this License will not = +have their licenses terminated so long as such parties remain in full com= +pliance.=0A=0A5. You are not required to accept this License, since you h= +ave not signed it. However, nothing else grants you permission to modify = +or distribute the Program or its derivative works. These actions are proh= +ibited by law if you do not accept this License. Therefore, by modifying = +or distributing the Program (or any work based on the Program), you indic= +ate your acceptance of this License to do so, and all its terms and condi= +tions for copying, distributing or modifying the Program or works based o= +n it.=0A=0A6. Each time you redistribute the Program (or any work based o= +n the Program), the recipient automatically receives a license from the o= +riginal licensor to copy, distribute or modify the Program subject to the= +se terms and conditions. You may not impose any further restrictions on t= +he recipients' exercise of the rights granted herein. You are not respons= +ible for enforcing compliance by third parties to this License.=0A=0A7. I= +f, as a consequence of a court judgment or allegation of patent infringem= +ent or for any other reason (not limited to patent issues), conditions ar= +e imposed on you (whether by court order, agreement or otherwise) that co= +ntradict the conditions of this License, they do not excuse you from the = +conditions of this License. If you cannot distribute so as to satisfy sim= +ultaneously your obligations under this License and any other pertinent o= +bligations, then as a consequence you may not distribute the Program at a= +ll. For example, if a patent license would not permit royalty-free redist= +ribution of the Program by all those who receive copies directly or indir= +ectly through you, then the only way you could satisfy both it and this L= +icense would be to refrain entirely from distribution of the Program.=0A=0A= +If any portion of this section is held invalid or unenforceable under any= + particular circumstance, the balance of the section is intended to apply= + and the section as a whole is intended to apply in other circumstances.=0A= +=0AIt is not the purpose of this section to induce you to infringe any pa= +tents or other property right claims or to contest validity of any such c= +laims; this section has the sole purpose of protecting the integrity of t= +he free software distribution system, which is implemented by public lice= +nse practices. Many people have made generous contributions to the wide r= +ange of software distributed through that system in reliance on consisten= +t application of that system; it is up to the author/donor to decide if h= +e or she is willing to distribute software through any other system and a= + licensee cannot impose that choice.=0A=0AThis section is intended to mak= +e thoroughly clear what is believed to be a consequence of the rest of th= +is License.=0A=0A8. If the distribution and/or use of the Program is rest= +ricted in certain countries either by patents or by copyrighted interface= +s, the original copyright holder who places the Program under this Licens= +e may add an explicit geographical distribution limitation excluding thos= +e countries, so that distribution is permitted only in or among countries= + not thus excluded. In such case, this License incorporates the limitatio= +n as if written in the body of this License.=0A=0A9. The Free Software Fo= +undation may publish revised and/or new versions of the General Public Li= +cense from time to time. Such new versions will be similar in spirit to t= +he present version, but may differ in detail to address new problems or c= +oncerns.=0A=0AEach version is given a distinguishing version number. If t= +he Program specifies a version number of this License which applies to it= + and "any later version", you have the option of following the terms and = +conditions either of that version or of any later version published by th= +e Free Software Foundation. If the Program does not specify a version num= +ber of this License, you may choose any version ever published by the Fre= +e Software Foundation.=0A=0A10. If you wish to incorporate parts of the P= +rogram into other free programs whose distribution conditions are differe= +nt, write to the author to ask for permission. For software which is copy= +righted by the Free Software Foundation, write to the Free Software Found= +ation; we sometimes make exceptions for this. Our decision will be guided= + by the two goals of preserving the free status of all derivatives of our= + free software and of promoting the sharing and reuse of software general= +ly.=0A=0ANO WARRANTY=0A=0A11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHA= +RGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APP= +LICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDER= +S AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF AN= +Y KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE I= +MPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE= +=2E THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS W= +ITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL N= +ECESSARY SERVICING, REPAIR OR CORRECTION.=0A=0A12. IN NO EVENT UNLESS REQ= +UIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER= +, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PE= +RMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECI= +AL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILI= +TY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA = +BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR = +A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUC= +H HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAG= +ES.=0A=0AEND OF TERMS AND CONDITIONS=0A=0AHow to Apply These Terms to You= +r New Programs=0A=0AIf you develop a new program, and you want it to be o= +f the greatest possible use to the public, the best way to achieve this i= +s to make it free software which everyone can redistribute and change und= +er these terms.=0A=0ATo do so, attach the following notices to the progra= +m. It is safest to attach them to the start of each source file to most e= +ffectively convey the exclusion of warranty; and each file should have at= + least the "copyright" line and a pointer to where the full notice is fou= +nd.=0A=0A=09one line to give the program's name and an idea of what it do= +es.=0A=09Copyright (C) yyyy name of author=0A=0A=09This program is free = +software; you can redistribute it and/or=0A=09modify it under the terms o= +f the GNU General Public License=0A=09as published by the Free Software F= +oundation; either version 2=0A=09of the License, or (at your option) any = +later version.=0A=0A=09This program is distributed in the hope that it wi= +ll be useful,=0A=09but WITHOUT ANY WARRANTY; without even the implied war= +ranty of=0A=09MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See t= +he=0A=09GNU General Public License for more details.=0A=0A=09You should h= +ave received a copy of the GNU General Public License=0A=09along with thi= +s program; if not, write to the Free Software=0A=09Foundation, Inc., 59 T= +emple Place - Suite 330, Boston, MA 02111-1307, USA.=0A=0AAlso add infor= +mation on how to contact you by electronic and paper mail.=0A=0AIf the pr= +ogram is interactive, make it output a short notice like this when it sta= +rts in an interactive mode:=0A=0A=09Gnomovision version 69, Copyright (C)= + year name of author=0A=09Gnomovision comes with ABSOLUTELY NO WARRANTY; = +for details=0A=09type `show w'. This is free software, and you are welco= +me=0A=09to redistribute it under certain conditions; type `show c'=20=0A=09= +for details.=0A=0AThe hypothetical commands `show w' and `show c' should = +show the appropriate parts of the General Public License. Of course, the = +commands you use may be called something other than `show w' and `show c'= +; they could even be mouse-clicks or menu items--whatever suits your prog= +ram.=0A=0AYou should also get your employer (if you work as a programmer)= + or your school, if any, to sign a "copyright disclaimer" for the program= +, if necessary. Here is a sample; alter the names:=0A=0A=09Yoyodyne, Inc.= +, hereby disclaims all copyright=0A=09interest in the program `Gnomovisio= +n'=0A=09(which makes passes at compilers) written=20=0A=09by James Hacker= +=2E=0A=0A=09signature of Ty Coon, 1 April 1989=0A=09Ty Coon, President of= + Vice=0A=0AThis General Public License does not permit incorporating your= + program into proprietary programs. If your program is a subroutine libra= +ry, you may consider it more useful to permit linking proprietary applica= +tions with the library. If this is what you want to do, use the GNU Lesse= +r General Public License instead of this License.=0A \ No newline at end of file diff --git a/tests/encoding/test-suites/encode/gpl.uuencode b/tests/encoding/test-suites/encode/gpl.uuencode new file mode 100644 index 00000000..a136f7cc --- /dev/null +++ b/tests/encoding/test-suites/encode/gpl.uuencode @@ -0,0 +1,395 @@ +begin 644 no_name +M1TY5($=%3D5204P@4%5"3$E#($Q)0T5.4T4*"E9E2!Y;W5R(&9R +M965D;VT@=&\@6]U2!'96YE6]U +M(&AA=F4@=&AE(&9R965D;VT@=&\@9&ES=')I8G5T92!C;W!I97,@;V8@9G)E +M92!S;V9T=V%R92 H86YD(&-H87)G92!F;W(@=&AI6]U6]U +M('1H97-E(')I9VAT6]U('1O('-U6]U(&EF('EO=2!D:7-T6]U(&1I6]U +M(&UU7)I9VAT('1H92!S;V9T=V%R92P@86YD("@R*2!O9F9E +M2!T:&4@ +M2!P2!F6]N92=S(&9R964@=7-E(&]R(&YO="!L:6-E;G-E9"!A +M="!A;&PN"@I4:&4@<')E8VES92!T97)M6EN9RP@9&ES=')I8G5T:6]N(&%N9"!M;V1I9FEC871I;VX@9F]L;&]W +M+@H*5$5235,@04Y$($-/3D1)5$E/3E,@1D]2($-/4%E)3D2!T:&4@8V]P>7)I9VAT(&AO;&1E2!D97)I=F%T:79E('=O7)I +M9VAT(&QA=SH@=&AA="!I6EN9RP@9&ES=')I +M8G5T:6]N(&%N9"!M;V1I9FEC871I;VX@87)E(&YO="!C;W9E2!A2!A;F0@9&ES=')I8G5T92!V97)B871I;2!C;W!I97,@;V8@=&AE +M(%!R;V=R86TG2!A +M;F0@87!P2!P=6)L:7-H(&]N(&5A8V@@8V]P>2!A;B!A<'!R +M;W!R:6%T92!C;W!Y2!W +M87)R86YT>3L@86YD(&=I=F4@86YY(&]T:&5R(')E8VEP:65N=',@;V8@=&AE +M(%!R;V=R86T@82!C;W!Y(&]F('1H:7,@3&EC96YS92!A;&]N9R!W:71H('1H +M92!02!C:&%R9V4@82!F964@9F]R('1H92!P:'ES +M:6-A;"!A8W0@;V8@=')A;G-F97)R:6YG(&$@8V]P>2P@86YD('EO=2!M87D@ +M870@>6]U&-H86YG92!F;W(@82!F964N"@HR+B!9;W4@;6%Y(&UO9&EF>2!Y;W5R(&-O +M<'D@;W(@8V]P:65S(&]F('1H92!02!P;W)T:6]N(&]F +M(&ET+"!T:'5S(&9O2!A;F0@9&ES=')I8G5T92!S=6-H(&UO9&EF:6-A=&EO;G,@ +M;W(@=V]R:R!U;F1E2!W;W)K('1H870@ +M>6]U(&1I2!W:&5N(')U;BP@>6]U(&UU2!O9B!T:&ES($QI8V5N&-E<'1I +M;VXZ(&EF('1H92!06]U2!A;F0@9&ES=')I8G5T92!T:&4@4')O9W)A;2 H +M;W(@82!W;W)K(&)A2!U6]U7-I8V%L;'D@<&5R9F]R;6EN9R!S +M;W5R8V4@9&ES=')I8G5T:6]N+"!A(&-O;7!L971E(&UA8VAI;F4M2!O9B!T:&4@8V]R&5C=71A8FQE +M(&9O&5C=71A8FQE('=O +M2!A +M&5C=71A8FQE+B!(;W=E=F5R+"!A2!F;W)M*2!W +M:71H('1H92!M86IO&5C=71A8FQE(')U;G,L('5N;&5S2!N +M;W0@8V]P>2P@;6]D:69Y+"!S=6)L:6-E;G-E+"!O2P@ +M;6]D:69Y+"!S=6)L:6-E;G-E(&]R(&1I2!T97)M:6YA=&4@>6]U +M2!O6]U6]U(')E9&ES=')I8G5T92!T:&4@4')O9W)A;2 H;W(@86YY('=O2!T:&ER9"!P87)T:65S('1O('1H:7,@3&EC96YS92X* +M"C6]U(&UA>2!N;W0@9&ES=')I +M8G5T92!T:&4@4')O9W)A;2!A="!A;&PN($9O&%M<&QE+"!I9B!A('!A +M=&5N="!L:6-E;G-E('=O=6QD(&YO="!P97)M:70@2UF2!T:')O=6=H +M('EO=2P@=&AE;B!T:&4@;VYL>2!W87D@>6]U(&-O=6QD('-A=&ES9GD@8F]T +M:"!I="!A;F0@=&AI2!A;F0@=&AE('-E8W1I;VX@87,@82!W:&]L92!I2!I;B!O=&AE6]U('1O +M(&EN9G)I;F=E(&%N>2!P871E;G1S(&]R(&]T:&5R('!R;W!E2!S=6-H(&-L +M86EM2!O9B!T:&4@9G)E92!S;V9T=V%R92!D +M:7-T2!P +M=6)L:6,@;&EC96YS92!P7-T96T@:6X@ +M2!O=&AE7-T96T@86YD(&$@;&EC96YS964@8V%N +M;F]T(&EM<&]S92!T:&%T(&-H;VEC92X*"E1H:7,@2!P871E;G1S(&]R(&)Y(&-O<'ER:6=H=&5D(&EN=&5R9F%C +M97,L('1H92!O2!A9&0@86X@97AP +M;&EC:70@9V5O9W)A<&AI8V%L(&1I2!D:69F +M97(@:6X@9&5T86EL('1O(&%D9')E2!A('9E6]U(&UA>2!C:&]O2!T:&4@1G)E92!3;V9T=V%R92!& +M;W5N9&%T:6]N+"!W&-E<'1I;VYS(&9O2!T;R!A8VAI979E('1H:7,@:7,@=&\@;6%K92!I="!F +M2!C;VYV97D@=&AE(&5X +M8VQU3L@86YD(&5A8V@@9FEL92!S:&]U;&0@:&%V +M92!A="!L96%S="!T:&4@(F-O<'ER:6=H="(@;&EN92!A;F0@82!P;VEN=&5R +M('1O('=H97)E('1H92!F=6QL(&YO=&EC92!I7D@(&YA;64@;V8@875T +M:&]R"@H)5&AI2!T:&4@1G)E92!3;V9T=V%R92!&;W5N9&%T:6]N.R!E:71H +M97(@=F5R7!O=&AE=&EC86P@8V]M;6%N9',@8'-H;W<@=R<@ +M86YD(&!S:&]W(&,G('-H;W5L9"!S:&]W('1H92!A<'!R;W!R:6%T92!P87)T +M6]U('5S92!M87D@8F4@8V%L;&5D('-O;65T:&EN9R!O +M=&AE2!C;W5L9"!E +M=F5N(&)E(&UO=7-E+6-L:6-K6]U +M2P@=&\@7)I9VAT"@EI;G1E2!#;V]N+"!0 +M=00=00=00-=00=00=00=00=00=00=00=1F=00=00= +=00J=00=00=00+=00=00=00=3D=00=00=00=00=00=00=00/=00=00=00#=00=00=00!=00=00= +=00I=00=00=00W=00=00=009=00=00=00=00=00=00=000=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00P=00=00=00E=00=00=00^=00=00=00=00=00=00=00[=00=00=00=0F= +=00=00=00]=00=00=00a=00=00=00=00=00=00=00=0B=00=00=00=1C=00=00=00_=00=00=00= +=16=00=00=00=00=00=00=00*=00=00=00=00=00=00=00'=00=00=00=00=00=00=00=0A=00= +=00=00\=00=00=00=00=00=00=00N=00=00=00Z=00=00=00=19=00=00=00=07=00=00=00=01= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00e=00=00=006=00=00=00L=00=00=00= +=00=00=00=00=00=00=00=00Y=00=00=00=00=00=00=00=00=00=00=00C=00=00=00X=00=00= +=00b=00=00=00B=00=00=00O=00=00=00=00=00=00=00`=00=00=00Q=00=00=00(=00=00=00= +=00=00=00=00=1A=00=00=00=00=00=00=00$=00=00=00=00=00=00=00R=00=00=005=00=00= +=00%=00=00=00 =00=00=00F=00=00=00=1B=00=00=00=00=00=00=00=00=00=00=00=04=00= +=00=00c=00=00=00=00=00=00=00f=00=00=008=00=00=00d=00=00=00=00=00=00=00=08= +=00=00=00D=00=00=00K=00=00=00U=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=11=00=00=00=12=00=00=00=00=00=00=00=00=00=00= +=00=02=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=13=00=00=00=00=00=00=00=14=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=10=00=00=00=1D=00=00=00,=00=00= +=00=00=00=00=00&=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00"=00=00=00=17= +=00=00=00=0D=00=00=00=00=00=00=00=03=00=00=00=00=00=00=00=0C=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=007=00=00= +=00=00=00=00=00=00=00=00=00?=00=00=00=00=00=00=00)=00=00=00=00=00=00=00=00= +=00=00=00.=00=00=00H=00=00=00:=00=00=00=1E=00=00=00=09=00=00=00=00=00=00=00= +=00=00=00=004=00=00=00=06=00=00=00=05=00=00=00=15=00=00=00S=00=00=00=18=00= +=00=00=00=00=00=00M=00=00=00=00=00=00=00=00=00=00=00G=00=00=00=00=00=00=00= +@=00=00=00T=00=00=00=00=00=00=00=00=00=00=002=00=00=00=00=00=00=00A=00=00= +=00=00=00=00=00=00=00=00=00=0E=00=00=003=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00v=01=00=00=D8=93=04=08<=00=00=00=12=00=00=00|=03=00= +=00=E8=93=04=08$=01=00=00=12=00=00=003=01=00=00=F8=93=04=08*=00=00=00=12=00= +=00=00I=00=00=00=08=94=04=08:=00=00=00=12=00=00=00=99=02=00=00=18=94=04=08= +S=00=00=00=12=00=00=00=F5=00=00=00(=94=04=08=F1=00=00=00=12=00=00=00=D4=03= +=00=008=94=04=08g=01=00=00=12=00=00=00=7F=01=00=00H=94=04=08f=00=00=00=12= +=00=00=00b=00=00=00=E4=A3=05=08=00=00=00=00=11=00=F1=FF=EB=01=00=00X=94=04= +=08q=00=00=00=12=00=00=00=FF=01=00=00h=94=04=08=90=00=00=00=12=00=00=00=15= +=02=00=00x=94=04=08|=00=00=00=12=00=00=00=BA=02=00=00=88=94=04=088=00=00=00= +=12=00=00=00:=02=00=00=98=94=04=08=9C=00=00=00=12=00=00=00=E1=02=00=00=A8= +=94=04=08?=00=00=00=12=00=00=002=02=00=00=B8=94=04=08#=00=00=00=12=00=00=00= +G=01=00=00=C8=94=04=08=F6=00=00=00=12=00=00=00I=02=00=00=D8=94=04=08L=00=00= +=00=12=00=00=00=D3=02=00=00=E8=94=04=08[=01=00=00=12=00=00=00L=03=00=00=F8= +=94=04=08=A3=00=00=00=12=00=00=00=13=03=00=00=08=95=04=08=FF=05=00=00=12=00= +=00=00=FD=03=00=00=18=95=04=08E=00=00=00=12=00=00=00=E8=02=00=00(=95=04=08= +$=01=00=00=12=00=00=00\=03=00=008=95=04=089=00=00=00=12=00=00=00>=01=00=00= +H=95=04=08$=01=00=00=12=00=00=00=C3=01=00=00X=95=04=08=FD=04=00=00=12=00=00= +=00=D4=01=00=00h=95=04=08:=00=00=00=12=00=00=00=8C=00=00=00=B0=93=04=08=00= +=00=00=00=12=00=0A=00=BC=00=00=00x=95=04=08}=00=00=00=12=00=00=00=92=01=00= +=00=88=95=04=08=B5=01=00=00=12=00=00=00C=03=00=00=98=95=04=08G =00=00=12=00= +=00=00=1F=01=00=00=A8=95=04=08=8B=00=00=00=12=00=00=00=EE=00=00=00`=A6=05= +=08=04=00=00=00=11=00=17=00=1D=03=00=00d=A6=05=08=04=00=00=00=11=00=17=00= +=CE=01=00=00=B8=95=04=08=0D=02=00=00=12=00=00=00=C5=00=00=00=C8=95=04=08=8D= +=00=00=00=12=00=00=00Z=02=00=00=D8=95=04=08=F0=01=00=00=12=00=00=00m=03=00= +=00=E8=95=04=08'=00=00=00=12=00=00=00=15=00=00=00=F8=95=04=08@=00=00=00=12= +=00=00=00=CD=03=00=00=08=96=04=08=1B=00=00=00=12=00=00=00=B9=01=00=00=18=96= +=04=08J=00=00=00=12=00=00=00b=02=00=00(=96=04=08=0A=00=00=00=12=00=00=00=FF= +=00=00=008=96=04=08=92=08=00=00=12=00=00=00W=01=00=00H=96=04=08=86=06=00=00= +=12=00=00=00i=01=00=00X=96=04=08u=01=00=00=12=00=00=00=8A=01=00=00h=96=04= +=08=0E=02=00=00=12=00=00=00=B0=03=00=00x=96=04=08=A0=03=00=00=12=00=00=00= +N=01=00=00=88=96=04=08=D3=00=00=00=12=00=00=00p=02=00=00=98=96=04=08=C0=00= +=00=00=12=00=00=00=A3=02=00=00=A8=96=04=08=10=01=00=00=12=00=00=00=11=04=00= +=00D=A6=05=08=00=00=00=00=10=00=F1=FF=BB=03=00=00=B8=96=04=08=02=01=00=00= +=12=00=00=00=EF=03=00=00=C8=96=04=08F=00=00=00=12=00=00=00=88=02=00=00=D8= +=96=04=08=BA=01=00=00=12=00=00=00q=03=00=00=E8=96=04=08=F8=00=00=00=12=00= +=00=003=02=00=00=F8=96=04=089=00=00=00=12=00=00=00=92=00=00=00=D0Y=05=08=00= +=00=00=00=12=00=0D=00o=01=00=00=08=97=04=08F=00=00=00=12=00=00=00=0C=03=00= +=00=18=97=04=08=80=01=00=00=12=00=00=00=8D=03=00=00(=97=04=08q=00=00=00=12= +=00=00=00=E3=01=00=008=97=04=08=B9=01=00=00=12=00=00=00=90=02=00=00H=97=04= +=08W=00=00=00=12=00=00=00'=01=00=00X=97=04=08J=00=00=00=12=00=00=00=DB=03= +=00=00h=97=04=08=86=00=00=00=12=00=00=00=AD=02=00=00x=97=04=08:=00=00=00=12= +=00=00=00k=00=00=00=88=97=04=08=00=01=00=00=12=00=00=00h=02=00=00=98=97=04= +=08=F5=0B=00=00=12=00=00=00=CB=02=00=00=A8=97=04=08=B0=01=00=00=12=00=00=00= +*=03=00=00=B8=97=04=08=10=01=00=00=12=00=00=00=13=01=00=00=C8=97=04=08N=00= +=00=00=12=00=00=00=1B=02=00=00=D8=97=04=08=3D=00=00=00=12=00=00=00=98=00=00= +=00=E8=97=04=08=12=01=00=00=12=00=00=00=F1=02=00=00=F8=97=04=08$=01=00=00= +=12=00=00=00=05=04=00=00=08=98=04=08=D1=00=00=00=12=00=00=00=0E=02=00=00=18= +=98=04=08=E0=02=00=00=12=00=00=00=DA=01=00=00(=98=04=08=D3=00=00=00=12=00= +=00=00=0A=04=00=00D=A6=05=08=00=00=00=00=10=00=F1=FF=A6=00=00=00=D0=A4=05= +=08=00=00=00=00=11=00=F1=FF=C0=00=00=008=98=04=08=B1=00=00=00=12=00=00=00= +=1D=04=00=00=F0=A9=05=08=00=00=00=00=10=00=F1=FF=E8=00=00=00H=98=04=08<=00= +=00=00=12=00=00=00=A7=01=00=00X=98=04=08f=01=00=00=12=00=00=00=99=01=00=00= +h=98=04=085=00=00=00=12=00=00=00=C4=02=00=00x=98=04=08R=00=00=00=12=00=00= +=00U=00=00=00=88=98=04=08J=01=00=00=12=00=00=00=02=03=00=00=98=98=04=08D=00= +=00=00=12=00=00=00=F5=03=00=00=A8=98=04=08=19=00=00=00=12=00=00=00x=00=00= +=00=B8=98=04=08G=00=00=00=12=00=00=00x=02=00=00=C8=98=04=08=A6=00=00=00=12= +=00=00=00=85=03=00=00=D8=98=04=08$=00=00=00=12=00=00=00=80=02=00=00=E8=98= +=04=08=A2=00=00=00=12=00=00=00=A0=01=00=00h=A6=05=08=04=00=00=00=11=00=17= +=00=A1=03=00=00=04Z=05=08=04=00=00=00=11=00=0E=00=0C=00=00=00=F8=98=04=08= +o=00=00=00=12=00=00=00=FA=02=00=00=08=99=04=084=00=00=00=12=00=00=00$=03=00= +=00=18=99=04=08=AC=00=00=00=12=00=00=00=1A=00=00=00=00=00=00=00=00=00=00=00= + =00=00=005=03=00=00(=99=04=08q=00=00=00=12=00=00=00S=02=00=00l=A6=05=08=04= +=00=00=00=11=00=17=00=E4=03=00=008=99=04=08=10=01=00=00=12=00=00=00.=00=00= +=00=00=00=00=00=00=00=00=00 =00=00=00=E1=00=00=00H=99=04=080=00=00=00=12=00= +=00=00=00librt.so.1=00clock_gettime=00_Jv_RegisterClasses=00__gmon_start_= +_=00libacl.so.1=00acl_entries=00acl_get_file=00_DYNAMIC=00acl_set_file=00= +acl_delete_def_file=00_init=00_fini=00acl_from_text=00_GLOBAL_OFFSET_TABL= +E_=00acl_free=00acl_extended_file=00libc.so.6=00strcpy=00ioctl=00stdout=00= +readdir64=00__strtoull_internal=00sigemptyset=00memmove=00getopt_long=00_= +_fpending=00getgrgid=00getenv=00iswcntrl=00__strtol_internal=00qsort=00me= +mcpy=00readlink=00__overflow=00mbrtowc=00malloc=00isatty=00optarg=00_obst= +ack_newchunk=00sigaddset=00localeconv=00abort=00chmod=00iswprint=00strrch= +r=00__ctype_tolower_loc=00_obstack_begin=00calloc=00write=00__ctype_get_m= +b_cur_max=00fprintf=00fputs_unlocked=00dcgettext=00optind=00fnmatch=00dir= +fd=00strcoll=00strncmp=00strncpy=00wcwidth=00realloc=00__strdup=00sigacti= +on=00__xstat64=00gettimeofday=00localtime=00memset=00opendir=00__assert_f= +ail=00strcmp=00getpwuid=00getpwnam=00sprintf=00__mempcpy=00fclose=00setlo= +cale=00stderr=00error=00__lxstat64=00__ctype_b_loc=00strftime=00fwrite_un= +locked=00__errno_location=00bindtextdomain=00getgrnam=00_setjmp=00__ctype= +_toupper_loc=00_IO_stdin_used=00strverscmp=00__libc_start_main=00strlen=00= +strchr=00closedir=00__fxstat64=00raise=00mbsinit=00__cxa_atexit=00_edata=00= +__bss_start=00_end=00GLIBC_2.2=00ACL_1.0=00GLIBC_2.2.3=00GLIBC_2.1.3=00GL= +IBC_2.1=00GLIBC_2.3=00GLIBC_2.0=00=00=00=02=00=02=00=03=00=04=00=02=00=03= +=00=02=00=02=00=01=00=05=00=02=00=02=00=02=00=06=00=02=00=02=00=02=00=02=00= +=02=00=06=00=02=00=07=00=02=00=02=00=02=00=03=00=02=00=01=00=04=00=02=00=02= +=00=02=00=02=00=02=00=02=00=04=00=08=00=02=00=02=00=02=00=02=00=02=00=02=00= +=02=00=02=00=02=00=06=00=02=00=02=00=03=00=01=00=02=00=02=00=02=00=02=00=02= +=00=01=00=02=00=06=00=05=00=02=00=02=00=02=00=02=00=02=00=04=00=02=00=02=00= +=03=00=02=00=02=00=04=00=02=00=02=00=02=00=02=00=01=00=01=00=02=00=01=00=02= +=00=02=00=02=00=02=00=04=00=02=00=02=00=04=00=02=00=02=00=02=00=02=00=01=00= +=09=00=02=00=02=00=00=00=05=00=02=00=03=00=00=00=02=00=01=00=01=00=01=00=00= +=00=10=00=00=00 =00=00=00=12ii=0D=00=00=09=00"=04=00=00=00=00=00=00=01=00= +=01=00=3D=00=00=00=10=00=00=00 =00=00=00P$=82=05=00=00=04=00,=04=00=00=00= +=00=00=00=01=00=06=00=D7=00=00=00=10=00=00=00=00=00=00=00s=1Ai=09=00=00=08= +=004=04=00=00=10=00=00=00s=1Fi=09=00=00=07=00@=04=00=00=10=00=00=00=11ii=0D= +=00=00=06=00L=04=00=00=10=00=00=00=13ii=0D=00=00=05=00V=04=00=00=10=00=00= +=00=12ii=0D=00=00=03=00"=04=00=00=10=00=00=00=10ii=0D=00=00=02=00`=04=00=00= +=00=00=00=00@=A6=05=08=06e=00=00`=A6=05=08=05!=00=00d=A6=05=08=05"=00=00h= +=A6=05=08=05\=00=00l=A6=05=08=05c=00=00=DC=A4=05=08=07=01=00=00=E0=A4=05=08= +=07=02=00=00=E4=A4=05=08=07=03=00=00=E8=A4=05=08=07=04=00=00=EC=A4=05=08=07= +=05=00=00=F0=A4=05=08=07=06=00=00=F4=A4=05=08=07=07=00=00=F8=A4=05=08=07=08= +=00=00=FC=A4=05=08=07=0A=00=00=00=A5=05=08=07=0B=00=00=04=A5=05=08=07=0C=00= +=00=08=A5=05=08=07=0D=00=00=0C=A5=05=08=07=0E=00=00=10=A5=05=08=07=0F=00=00= +=14=A5=05=08=07=10=00=00=18=A5=05=08=07=11=00=00=1C=A5=05=08=07=12=00=00 = +=A5=05=08=07=13=00=00$=A5=05=08=07=14=00=00(=A5=05=08=07=15=00=00,=A5=05=08= +=07=16=00=000=A5=05=08=07=17=00=004=A5=05=08=07=18=00=008=A5=05=08=07=19=00= +=00<=A5=05=08=07=1A=00=00@=A5=05=08=07=1B=00=00D=A5=05=08=07=1D=00=00H=A5= +=05=08=07=1E=00=00L=A5=05=08=07=1F=00=00P=A5=05=08=07 =00=00T=A5=05=08=07= +#=00=00X=A5=05=08=07$=00=00\=A5=05=08=07%=00=00`=A5=05=08=07&=00=00d=A5=05= +=08=07'=00=00h=A5=05=08=07(=00=00l=A5=05=08=07)=00=00p=A5=05=08=07*=00=00= +t=A5=05=08=07+=00=00x=A5=05=08=07,=00=00|=A5=05=08=07-=00=00=80=A5=05=08=07= +=2E=00=00=84=A5=05=08=07/=00=00=88=A5=05=08=070=00=00=8C=A5=05=08=071=00=00= +=90=A5=05=08=072=00=00=94=A5=05=08=074=00=00=98=A5=05=08=075=00=00=9C=A5=05= +=08=076=00=00=A0=A5=05=08=077=00=00=A4=A5=05=08=078=00=00=A8=A5=05=08=07:= +=00=00=AC=A5=05=08=07;=00=00=B0=A5=05=08=07<=00=00=B4=A5=05=08=07=3D=00=00= +=B8=A5=05=08=07>=00=00=BC=A5=05=08=07?=00=00=C0=A5=05=08=07@=00=00=C4=A5=05= +=08=07A=00=00=C8=A5=05=08=07B=00=00=CC=A5=05=08=07C=00=00=D0=A5=05=08=07D= +=00=00=D4=A5=05=08=07E=00=00=D8=A5=05=08=07F=00=00=DC=A5=05=08=07G=00=00=E0= +=A5=05=08=07H=00=00=E4=A5=05=08=07I=00=00=E8=A5=05=08=07J=00=00=EC=A5=05=08= +=07K=00=00=F0=A5=05=08=07L=00=00=F4=A5=05=08=07O=00=00=F8=A5=05=08=07Q=00= +=00=FC=A5=05=08=07R=00=00=00=A6=05=08=07S=00=00=04=A6=05=08=07T=00=00=08=A6= +=05=08=07U=00=00=0C=A6=05=08=07V=00=00=10=A6=05=08=07W=00=00=14=A6=05=08=07= +X=00=00=18=A6=05=08=07Y=00=00=1C=A6=05=08=07Z=00=00 =A6=05=08=07[=00=00$=A6= +=05=08=07^=00=00(=A6=05=08=07_=00=00,=A6=05=08=07`=00=000=A6=05=08=07b=00= +=004=A6=05=08=07d=00=008=A6=05=08=07f=00=00U=89=E5=83=EC=08=E8=C9=05=00=00= +=E80=06=00=00=E8=DB=C5=00=00=C9=C3=00=FF5=D4=A4=05=08=FF%=D8=A4=05=08=00=00= +=00=00=FF%=DC=A4=05=08h=00=00=00=00=E9=E0=FF=FF=FF=FF%=E0=A4=05=08h=08=00= +=00=00=E9=D0=FF=FF=FF=FF%=E4=A4=05=08h=10=00=00=00=E9=C0=FF=FF=FF=FF%=E8=A4= +=05=08h=18=00=00=00=E9=B0=FF=FF=FF=FF%=EC=A4=05=08h =00=00=00=E9=A0=FF=FF= +=FF=FF%=F0=A4=05=08h(=00=00=00=E9=90=FF=FF=FF=FF%=F4=A4=05=08h0=00=00=00=E9= +=80=FF=FF=FF=FF%=F8=A4=05=08h8=00=00=00=E9p=FF=FF=FF=FF%=FC=A4=05=08h@=00= +=00=00=E9`=FF=FF=FF=FF%=00=A5=05=08hH=00=00=00=E9P=FF=FF=FF=FF%=04=A5=05=08= +hP=00=00=00=E9@=FF=FF=FF=FF%=08=A5=05=08hX=00=00=00=E90=FF=FF=FF=FF%=0C=A5= +=05=08h`=00=00=00=E9 =FF=FF=FF=FF%=10=A5=05=08hh=00=00=00=E9=10=FF=FF=FF=FF= +%=14=A5=05=08hp=00=00=00=E9=00=FF=FF=FF=FF%=18=A5=05=08hx=00=00=00=E9=F0=FE= +=FF=FF=FF%=1C=A5=05=08h=80=00=00=00=E9=E0=FE=FF=FF=FF% =A5=05=08h=88=00=00= +=00=E9=D0=FE=FF=FF=FF%$=A5=05=08h=90=00=00=00=E9=C0=FE=FF=FF=FF%(=A5=05=08= +h=98=00=00=00=E9=B0=FE=FF=FF=FF%,=A5=05=08h=A0=00=00=00=E9=A0=FE=FF=FF=FF= +%0=A5=05=08h=A8=00=00=00=E9=90=FE=FF=FF=FF%4=A5=05=08h=B0=00=00=00=E9=80=FE= +=FF=FF=FF%8=A5=05=08h=B8=00=00=00=E9p=FE=FF=FF=FF%<=A5=05=08h=C0=00=00=00= +=E9`=FE=FF=FF=FF%@=A5=05=08h=C8=00=00=00=E9P=FE=FF=FF=FF%D=A5=05=08h=D0=00= +=00=00=E9@=FE=FF=FF=FF%H=A5=05=08h=D8=00=00=00=E90=FE=FF=FF=FF%L=A5=05=08= +h=E0=00=00=00=E9 =FE=FF=FF=FF%P=A5=05=08h=E8=00=00=00=E9=10=FE=FF=FF=FF%T= +=A5=05=08h=F0=00=00=00=E9=00=FE=FF=FF=FF%X=A5=05=08h=F8=00=00=00=E9=F0=FD= +=FF=FF=FF%\=A5=05=08h=00=01=00=00=E9=E0=FD=FF=FF=FF%`=A5=05=08h=08=01=00=00= +=E9=D0=FD=FF=FF=FF%d=A5=05=08h=10=01=00=00=E9=C0=FD=FF=FF=FF%h=A5=05=08h=18= +=01=00=00=E9=B0=FD=FF=FF=FF%l=A5=05=08h =01=00=00=E9=A0=FD=FF=FF=FF%p=A5=05= +=08h(=01=00=00=E9=90=FD=FF=FF=FF%t=A5=05=08h0=01=00=00=E9=80=FD=FF=FF=FF%= +x=A5=05=08h8=01=00=00=E9p=FD=FF=FF=FF%|=A5=05=08h@=01=00=00=E9`=FD=FF=FF=FF= +%=80=A5=05=08hH=01=00=00=E9P=FD=FF=FF=FF%=84=A5=05=08hP=01=00=00=E9@=FD=FF= +=FF=FF%=88=A5=05=08hX=01=00=00=E90=FD=FF=FF=FF%=8C=A5=05=08h`=01=00=00=E9= + =FD=FF=FF=FF%=90=A5=05=08hh=01=00=00=E9=10=FD=FF=FF=FF%=94=A5=05=08hp=01= +=00=00=E9=00=FD=FF=FF=FF%=98=A5=05=08hx=01=00=00=E9=F0=FC=FF=FF=FF%=9C=A5= +=05=08h=80=01=00=00=E9=E0=FC=FF=FF=FF%=A0=A5=05=08h=88=01=00=00=E9=D0=FC=FF= +=FF=FF%=A4=A5=05=08h=90=01=00=00=E9=C0=FC=FF=FF=FF%=A8=A5=05=08h=98=01=00= +=00=E9=B0=FC=FF=FF=FF%=AC=A5=05=08h=A0=01=00=00=E9=A0=FC=FF=FF=FF%=B0=A5=05= +=08h=A8=01=00=00=E9=90=FC=FF=FF=FF%=B4=A5=05=08h=B0=01=00=00=E9=80=FC=FF=FF= +=FF%=B8=A5=05=08h=B8=01=00=00=E9p=FC=FF=FF=FF%=BC=A5=05=08h=C0=01=00=00=E9= +`=FC=FF=FF=FF%=C0=A5=05=08h=C8=01=00=00=E9P=FC=FF=FF=FF%=C4=A5=05=08h=D0=01= +=00=00=E9@=FC=FF=FF=FF%=C8=A5=05=08h=D8=01=00=00=E90=FC=FF=FF=FF%=CC=A5=05= +=08h=E0=01=00=00=E9 =FC=FF=FF=FF%=D0=A5=05=08h=E8=01=00=00=E9=10=FC=FF=FF= +=FF%=D4=A5=05=08h=F0=01=00=00=E9=00=FC=FF=FF=FF%=D8=A5=05=08h=F8=01=00=00= +=E9=F0=FB=FF=FF=FF%=DC=A5=05=08h=00=02=00=00=E9=E0=FB=FF=FF=FF%=E0=A5=05=08= +h=08=02=00=00=E9=D0=FB=FF=FF=FF%=E4=A5=05=08h=10=02=00=00=E9=C0=FB=FF=FF=FF= +%=E8=A5=05=08h=18=02=00=00=E9=B0=FB=FF=FF=FF%=EC=A5=05=08h =02=00=00=E9=A0= +=FB=FF=FF=FF%=F0=A5=05=08h(=02=00=00=E9=90=FB=FF=FF=FF%=F4=A5=05=08h0=02=00= +=00=E9=80=FB=FF=FF=FF%=F8=A5=05=08h8=02=00=00=E9p=FB=FF=FF=FF%=FC=A5=05=08= +h@=02=00=00=E9`=FB=FF=FF=FF%=00=A6=05=08hH=02=00=00=E9P=FB=FF=FF=FF%=04=A6= +=05=08hP=02=00=00=E9@=FB=FF=FF=FF%=08=A6=05=08hX=02=00=00=E90=FB=FF=FF=FF= +%=0C=A6=05=08h`=02=00=00=E9 =FB=FF=FF=FF%=10=A6=05=08hh=02=00=00=E9=10=FB= +=FF=FF=FF%=14=A6=05=08hp=02=00=00=E9=00=FB=FF=FF=FF%=18=A6=05=08hx=02=00=00= +=E9=F0=FA=FF=FF=FF%=1C=A6=05=08h=80=02=00=00=E9=E0=FA=FF=FF=FF% =A6=05=08= +h=88=02=00=00=E9=D0=FA=FF=FF=FF%$=A6=05=08h=90=02=00=00=E9=C0=FA=FF=FF=FF= +%(=A6=05=08h=98=02=00=00=E9=B0=FA=FF=FF=FF%,=A6=05=08h=A0=02=00=00=E9=A0=FA= +=FF=FF=FF%0=A6=05=08h=A8=02=00=00=E9=90=FA=FF=FF=FF%4=A6=05=08h=B0=02=00=00= +=E9=80=FA=FF=FF=FF%8=A6=05=08h=B8=02=00=00=E9p=FA=FF=FF=00=00=00=00=00=00= +=00=001=ED^=89=E1=83=E4=F0PTRh=00Y=05=08h=A0X=05=08QVh=B0=9D=04=08=E87=FD= +=FF=FF=F4=90=90U=89=E5S=E8=00=00=00=00[=81=C3C=0B=01=00P=8B=83p=01=00=00=85= +=C0t=02=FF=D0=8B]=FC=C9=C3=90=90=90=90=90=90=90=90=90=90U=89=E5=83=EC=08=80= +=3Dp=A6=05=08=00u-=A1h=A2=05=08=8B=10=85=D2t=1B=8D=B6=00=00=00=00=83=C0=04= +=A3h=A2=05=08=FF=D2=A1h=A2=05=08=8B=10=85=D2u=EB=C6=05p=A6=05=08=01=C9=C3= +=89=F6U=89=E5=83=EC=08=A1=CC=A4=05=08=85=C0t!=B8=00=00=00=00=85=C0t=18=C7= +=04$=CC=A4=05=08=E8=ECe=FB=F7=8D=B6=00=00=00=00=8D=BF=00=00=00=00=89=EC]=C3= +=90=90=90=90=90=90=90=90=90=90=90=90U=89=E5=83=EC=18=89]=FC=8B=15=CC=A7=05= +=08=8B=0D=C8=A7=05=08=8B]=08=89=D0)=C8=83=F8=0FvQ=A1=D0=A7=05=08)=D0=83=F8= +=F0|(=89=1C$=8DB=F0=BA=10=00=00=00=89D$=04=A3=CC=A7=05=08=89T$=08=E8.=FB=FF= +=FF=89=D8=8B]=FC=89=EC]=C2=04=00=C7=04$=C0=A7=05=08=B9=F0=FF=FF=FF=89L$=04= +=E8=BE=FD=FF=FF=8B=15=CC=A7=05=08=EB=BB=C7=04$`j=05=08=B8/\=05=08=BB;\=05= +=08=89D$=0C=B8k=03=00=00=89D$=08=89\$=04=E8=1F=FA=FF=FF=8D=B4&=00=00=00=00= +U=89=E5WVS=83=EC=1C=8BM=0C=8BE=08=8BQ=0C=8By=08=89E=F0=89=D6)=FE=C1=EE=02= +=85=F6~^9=FA=0F=84=95=00=00=00=8BA=18=8BY=10=01=C2=89]=EC=F7=D0!=C2=89Q=0C= +=8BA=04)=C2)=C39=DA~=06=8BE=EC=89A=0C=8BA=0C=89A=08=A1`=A6=05=08=89D$=04=8B= +]=F0=89=1C$1=DB=E8^=F9=FF=FF9=F3|8=A1`=A6=05=08=8BP=14;P=18s=15=C6=02=0A=FF= +@=14=83=C4=1C[^_]=C3=8D=B4&=00=00=00=00=C7E=0C=0A=00=00=00=89E=08=83=C4=1C= +[^_]=E9=D2=F8=FF=FF=8B=04=9FC=C7=04$@\=05=08=89D$=04=E8n=FB=FF=FF9=F3|=E8= +=EB=AE=80I(=02=E9b=FF=FF=FF=8D=B4&=00=00=00=00U=89=E5=83=EC=18=8BE=08=8BM= +=0C=8BP=04=8B=00=89L$=081=C9=89L$=0C=89=04$=89T$=04=E8=19=BB=00=00=89=EC]= +=C3=90=8Dt&=00U=89=E5=83=EC=10=89}=FC=8B}=08=89u=F8=8Bu=0C=89]=F4=8B_=04=C6= +E=F3=00=8B=06=8BV=04=8B=0F=89=DE1=D61=C8=09=C6u#=8Bu=0C=8B_=0C=8BO=08=8BF= +=08=8BV=0C=89=DE1=C81=D6=09=C6u=0A=C6E=F3=01=8D=B6=00=00=00=00=0F=B6E=F3=8B= +]=F4=8Bu=F8=8B}=FC=89=EC]=C3=EB=0D=90=90=90=90=90=90=90=90=90=90=90=90=90= +U=89=E5]=E9=EF=FB=FF=FF=8D=B4&=00=00=00=00U=89=E5=83=EC(=89]=F4=8BU=0C=8B= +E=08=89u=F8=8B]=10=8Bu=14=89}=FC=89U=EC=89E=E8=C7=04$=10=00=00=00=E8c=A5=00= +=00=89=18=89=C7=8BU=EC=89p=04=8BE=E8=89W=0C=89G=08=A1=84=A6=05=08=89|$=04= +=89=04$=E8Oz=00=00=85=C0t%1=DB9=F8=0F=95=C3=85=DBu=10=89=D8=8Bu=F8=8B]=F4= +=8B}=FC=89=EC]=C3=90=89<$=E8p=FB=FF=FF=EB=E6=E8=A1=A4=00=00=90U=89=E5=83=EC= +=08=89]=FC=8B]=08=8B=03=85=C0u=1F=8BC=04=85=C0u=0E=89]=08=8B]=FC=89=EC]=E9= +A=FB=FF=FF=89=04$=E89=FB=FF=FF=EB=E8=89=04$=E8/=FB=FF=FF=EB=D7=90=8Dt&=00= +U=89=E5=83=EC=08=C7=04$=A0=A2=05=08=E8=EEC=00=00=85=C0t=0A=89=EC]=C3=8D=B6= +=00=00=00=00=C7=04$=A8=A2=05=08=E8=D4C=00=00=EB=E8=89=F6U=89=E5=81=EC=A8=00= +=00=00=89u=FC=8Bu=08=89]=F8=E8=B9=FF=FF=FF=83=FE=14tG1=C0=8D=9Dh=FF=FF=FF= +=89=85h=FF=FF=FF=8D=85l=FF=FF=FF=89=04$=E8P=FA=FF=FF=C7E=EC=00=00=00=001=C0= +=89D$=08=89\$=04=894$=E8=87=F6=FF=FF=894$=E8/=F9=FF=FF=8B]=F8=8Bu=FC=89=EC= +]=C3=BE=13=00=00=00=EB=E7=8D=B6=00=00=00=00U=89=E5WVS=81=EC=DC=01=00=00=BB= +E\=05=08=8BU=0C=83=E4=F0=8B=02=89\$=04=C7=04$=06=00=00=00=A3=EC=A9=05=08=E8= +*=F7=FF=FF=C7=04$F\=05=08=B9P\=05=08=89L$=04=E8=F5=F7=FF=FF=C7=04$F\=05=08= +=E8=E9=F8=FF=FF=C7=04$p=1D=05=08=E8U=BB=00=00=B8=01=00=00=001=D2=A3=08=A7= +=05=08=B8=01=00=00=00=A3=0C=A7=05=081=C0=89=15=1C=A7=05=08=8BU=08=A3=98=A6= +=05=08=8BE=0C=89=14$=89D$=04=E8=A0=05=00=00=89=C3=A1=D0=A6=05=08=85=C0=0F= +=85=96=04=00=00=83=3D=E0=A6=05=08=01=0F=84X=04=00=00=A1=E4=A6=05=08=85=C0= +=0F=85=E2=03=00=00=A1=A8=A6=05=081=D2=83=E8=03=83=F8=01v(=A1=A0=A6=05=08=85= +=C0t=1F=83=3D=E0=A6=05=08=05t=16=A1=B8=A6=05=08=85=C0u=0D=8B=3D=DC=A6=05=08= +=85=FFt=08=8Dv=00=BA=01=00=00=00=89=15=14=A7=05=081=C0=85=D2u$=8B5=E4=A6=05= +=08=85=F6u=15=8B=0D=D0=A6=05=08=85=C9u=0B=8B=15=CC=A6=05=08=85=D2t=06=90=B8= +=01=00=00=00=A3=18=A7=05=08=A1=C8=A6=05=08=85=C0=0F=85=0B=03=00=00=C7=04$= +=E0.=00=00=B8d=00=00=00=A3=8C=A6=05=08=E8=E3=A2=00=00=A3=88=A6=05=081=C0=A3= +=90=A6=05=08=E8=92=1E=00=00=8Bu=08)=DE=0F=95=C0=0F=B6=C0H!=05=08=A7=05=08= +;]=08=0F=8C=98=02=00=00=8B=0D=08=A7=05=08=85=C9t=3D=8B=15=E8=A6=05=08=85=D2= +=0F=84i=02=00=00=C7=04$b\=05=08=B8E\=05=08=89D$=0C=B8=01=00=00=00=89D$=08= +=B8=04=00=00=00=89D$=04=E8=A8=1E=00=00=90=8D=B4&=00=00=00=00=A1=90=A6=05=08= +=85=C0=0F=85=BD=01=00=00=83=FE=01=0F=87=AA=01=00=00=A1=98=A6=05=08=85=C0t= +k=8BH=08=85=C9u=0A1=D2=89=15=0C=A7=05=08=89=F6=85=C0tV=8D=9D8=FE=FF=FF=8D= +=B6=00=00=00=00=89=C6=8B@=08=A3=98=A6=05=08=A1=84=A6=05=08=85=C0t=0A=8B=06= +=85=C0=0F=84=03=01=00=00=8BF=04=89D$=04=8B=06=89=04$=E8=B2=16=00=00=894$=BE= +=01=00=00=00=E8=E5=FC=FF=FF=895=0C=A7=05=08=A1=98=A6=05=08=85=C0u=B6=8B=1D= +=C8=A6=05=08=85=DBuy=A1=D0=A6=05=08=85=C0uV=A1=84=A6=05=08=85=C0t=19=89=04= +$=E8"l=00=00=85=C0u=1A=A1=84=A6=05=08=89=04$=E8Qs=00=00=A1=1C=A7=05=08=89= +=04$=E8=CC=F7=FF=FF=C7=04$=00k=05=08=B8d\=05=08=89D$=0C=B8=C3=04=00=00=89= +D$=08=B8;\=05=08=89D$=04=E8=85=F4=FF=FF=C7=04$=A0=A2=05=08=E8A@=00=00=C7=04= +$=A8=A2=05=08=E85@=00=00=EB=90=C7=04$i\=05=08=B9@=A7=05=08=89L$=04=E8>=FA= +=FF=FF=C7=04$s\=05=08=BA=80=A7=05=08=89T$=04=E8)=FA=FF=FF=A1=FC=A6=05=08=89= +=04$=E8=9C=93=00=00=C7=04$@k=05=08=8B=04=85=A0=8B=05=08=89D$=04=E8-=F6=FF= +=FF=E94=FF=FF=FF=89=1C$=E8X=F9=FF=FF=A1=84=A6=05=08=83=EC=04=89\$=04=89=04= +$=E8Dw=00=00=85=C0t=15=89=04$=E8H=FB=FF=FF=894$=E8=D0=FB=FF=FF=E9=EC=FE=FF= +=FF=C7=04$=80\=05=08=B8d\=05=08=BF;\=05=08=89D$=0C=B8=A4=04=00=00=89D$=08= +=89|$=04=E92=FF=FF=FF=8Dt&=00=A1=98=A6=05=08=E9f=FE=FF=FF=E8q,=00=00=A1=E8= +=A6=05=08=85=C0tN=A1=90=A6=05=08=85=C0=0F=84(=FE=FF=FF=E8=96.=00=00=8B5=98= +=A6=05=08=85=F6=0F=84=92=FE=FF=FF=A1`=A6=05=08=8BP=14;P=18s=0E=C6=02=0A=FF= +@=14=FF=05(=A7=05=08=EB=AD=89=04$=BB=0A=00=00=00=89\$=04=E8=B4=F2=FF=FF=EB= +=E5=C7=04$E\=05=081=FF=89|$=04=E8=98#=00=00=EB=9E=C7=04$b\=05=081=C0=89D$= +=04=E8t=14=00=00=E9=AF=FD=FF=FF=B8E\=05=08=8BU=0C1=FF=89D$=0C=B8=01=00=00= +=00=89D$=08=89|$=04=8B=04=9AC=89=04$=E8(=1C=00=00;]=08|=D4=E97=FD=FF=FF=C7= +=04$@=A7=05=08=B88=98=04=081=FF=89D$=101=F6=B8=88=95=04=08=89D$=0C=89|$=08= +=89t$=04=E8L=F2=FF=FF=C7=04$=80=A7=05=081=C0=B98=98=04=08=89D$=08=BA=88=95= +=04=081=C0=89L$=10=89T$=0C=89D$=04=E8"=F2=FF=FF=E9=9C=FC=FF=FF=C7=04$=1E=00= +=00=001=D2=B9=A0=9B=04=08=89L$=08=BF@=9C=04=08=BE=D0=9B=04=08=89|$=10=89t= +$=0C=89T$=04=E88o=00=00=A3=84=A6=05=08=85=C0=0F=84V=01=00=00=C7=04$=C0=A7= +=05=08=B88=98=04=08=89D$=10=B8=88=95=04=08=89D$=0C1=C0=89D$=081=C0=89D$=04= +=E8=B9=F1=FF=FF=E9=B5=FB=FF=FF=A1=E8=A6=05=08=85=C0u=12=83=3D=CC=A6=05=08= +=01t=09=A1=A0=A6=05=08=85=C0u=0F=B8=02=00=00=00=A3=E0=A6=05=08=E9~=FB=FF=FF= +=B8=04=00=00=00=EB=EF=E8V=10=00=00=A1=D0=A6=05=08=85=C0=0F=84X=FB=FF=FF=E8= +=04:=00=00=A1=04=A3=05=08=85=C0u=14=8B=3D=FC=A2=05=08=85=FFt=15=8B5=A0=A6= +=05=08=85=F6u=0B=B9=01=00=00=00=89=0D=D8=A6=05=08=8D=85H=FE=FF=FF1=F6=89=04= +$=E8=94=F4=FF=FF=8D=B6=00=00=00=00=8D=BF=00=00=00=00=8B=04=B5=A4b=05=08=8D= +=95H=FE=FF=FFF=89=14$=89D$=04=E8=BE=F2=FF=FF=83=FE=06r=E1=FC=BA@=9D=04=08= +=8D=BD=CC=FE=FF=FF=89=95=C8=FE=FF=FF=B9 =00=00=00=8D=B5H=FE=FF=FF1=C0=F3=A5= +=89=85L=FF=FF=FF1=F6=90=8D=B4&=00=00=00=00=8B<=B5=A4b=05=08=8D=85X=FF=FF=FF= +=89D$=081=C0=89D$=04=89<$=E8i=F0=FF=FF=83=BDX=FF=FF=FF=01t=18=89<$1=C0=8D= +=95=C8=FE=FF=FF=89D$=08=89T$=04=E8H=F0=FF=FFF=83=FE=06r=BA=E9t=FA=FF=FF=E8= +=90=9D=00=00U1=C9=89=E5WVS=83=ECL=A1(=A3=05=08=C7E=D0=00=00=00=00=8Bu=08=8B= +}=0C=C7E=CC=00=00=00=00=83=F8=02=89=0D=F8=A6=05=08=0F=847=0D=00=00=83=F8=02= +=0F=8F=04=0D=00=00Ht=05=E8=94=F1=FF=FF=C7=04$=01=00=00=00=E88=F4=FF=FF=85= +=C0=0F=84=D7=0C=00=00=B8=02=00=00=00=A3=A0=A6=05=08=B8=01=00=00=00=A3=F8=A6= +=05=08=C7=04$=86\=05=081=C01=DB=A3=B4=A6=05=081=D2=B9=01=00=00=00=89=1D=A4= +=A6=05=081=C01=DB=A3=B8=A6=05=081=C0=A3=CC=A6=05=081=C0=A3=DC=A6=05=08=B8= +=01=00=00=00=A3=E0=A6=05=081=C0=89=0D=A8=A6=05=081=C9=89=15=AC=A6=05=081=D2= +=A3=E4=A6=05=081=C0=89=1D=E8=A6=05=08=89=0D=EC=A6=05=08=89=15=F0=A6=05=08= +=A3=F4=A6=05=08=E8=09=F0=FF=FF=85=C0=89=C3tB=89=1C$=B8=04=00=00=00=89D$=0C= +=B8=C0=8B=05=08=89D$=08=B8=A0=8B=05=08=89D$=04=E8Xt=00=00=85=C0=0F=88=D8=0B= +=00=00=C7=04$=00=00=00=00=8B=04=85=C0=8B=05=08=89D$=04=E8i=8F=00=00=C7=04= +$=94\=05=08=E8=B5=EF=FF=FF=89=04$=89=C31=D2=89T$=04=B9=C0=A6=05=08=89L$=08= +=E8=A4=86=00=00=A3=BC=A6=05=08=85=DB=0F=84v=0B=00=00=A1=C0=A6=05=08=8B=15= +=C4=A6=05=08=A3=90=A2=05=08=89=15=94=A2=05=08=C7=04$=A2\=05=08=B8P=00=00=00= +=A3=10=A7=05=08=E8c=EF=FF=FF=85=C0=89=C3t=09=808=00=0F=85=BE=0A=00=00=C7=04= +$=01=00=00=00=8DE=E0=BA=13T=00=00=89D$=08=89T$=04=E8=B8=F2=FF=FF@t=11=0F=B7= +E=E2f=85=C0t=08=0F=B7=C0=A3=10=A7=05=08=C7=04$=AA\=05=08=B8=08=00=00=00=A3= +=04=A7=05=08=E8=0E=EF=FF=FF=85=C0=0F=84=E3=09=00=00=8D=B4&=00=00=00=00=8D= +=BC'=00=00=00=00=89|$=041=C0=89D$=10=B8 _=05=08=89D$=0C=B8=80k=05=08=89D$= +=08=894$=E8d=F1=FF=FF=83=F8=FF=0F=84=BF=06=00=00=05=83=00=00=00=3D=0F=01=00= +=00=0F=873=04=00=00=FF$=85=C4b=05=08=BA=01=00=00=00=B8=01=00=00=00=89=15=EC= +=A6=05=08=A3=F0=A6=05=08=EB=A5=B8=BA\=05=08=BA=C0k=05=08=89D$=0C=B8F\=05=08= +=89D$=08=A1(=A3=05=08=89T$=10=BAM\=05=08=83=F8=01t=0F=83=F8=02=BA=C2\=05=08= +t=05=BA=C1\=05=08=89T$=04=A1`=A6=05=08=89=04$=E8=DB=99=00=00=C7=04$=00=00= +=00=00=E8=87=F1=FF=FF=C7=04$=00=00=00=00=90=8D=B4&=00=00=00=00=E8=DBC=00=00= +=E96=FF=FF=FF=A1=A0=A6=05=08=85=C0=0F=84)=FF=FF=FF=B8=01=00=00=00=A3=A0=A6= +=05=08=E9=1A=FF=FF=FF1=C0=A3=F0=A6=05=08=B8=01=00=00=00=A3=EC=A6=05=08=E9= +=04=FF=FF=FF=C7=04$=C7\=05=08=E8=18=16=00=00=C7=04$=C6\=05=08=E8=0C=16=00= +=00=E9=E7=FE=FF=FF=B8=02=00=00=00=EB=BC=B8=01=00=00=00=A3=C8=A6=05=08=E9=D1= +=FE=FF=FF=B8=01=00=00=00=A3=CC=A6=05=08=E9=C2=FE=FF=FF1=DB=89=1D=8C=A2=05= +=08=E9=B5=FE=FF=FF=B9=03=00=00=00=89=0D=E0=A6=05=08=E9=A5=FE=FF=FF=A1h=A6= +=05=08=89=04$=EB=AA=B8=05=00=00=00=A3=E0=A6=05=08=E9=8C=FE=FF=FF1=C0=89D$= +=04=C7=04$=00=00=00=00=E8=1A=8D=00=00=E9u=FE=FF=FF=B8=03=00=00=00=EB=E4=B8= +=01=00=00=00=A3=E4=A6=05=08=E9_=FE=FF=FF=B8=04=00=00=00=A3=A8=A6=05=08=C7= +E=CC=01=00=00=00=E9I=FE=FF=FF1=C01=C91=DB=89D$=10=8DE=D4=89D$=0C=A1h=A6=05= +=08=89\$=08=89L$=04=89=04$=E8=03=9C=00=00=85=C0u=11=8BE=D4=85=C0x=0A=A3=04= +=A7=05=08=E9=0E=FE=FF=FF=A1h=A6=05=08=89=04$=E8=91=95=00=00=C7=04$=00=00=00= +=00=89=C3=BA=05=00=00=00=89T$=08=B8=CA\=05=08=89D$=04=E8=E9=EC=FF=FF=89D$= +=081=C0=89D$=04=89\$=0C=C7=04$=01=00=00=00=E8=0F=F1=FF=FF=8BE=D4=EB=AA1=C0= +=E9a=FF=FF=FF=B8=02=00=00=00=E9W=FF=FF=FF=B8=04=00=00=00=E9=1D=FF=FF=FF=B8= +=01=00=00=00=A3=A4=A6=05=08=E9=98=FD=FF=FF=B8=01=00=00=00=A3=E8=A6=05=08=E9= +=89=FD=FF=FF=C7E=CC=01=00=00=00=8B=0D=A0=A6=05=08=B8=01=00=00=00=A3=EC=A6= +=05=081=DB=B8=01=00=00=00=A3=F0=A6=05=08=85=C9=89=1D=A8=A6=05=08t=141=D21= +=C0=89=15=B8=A6=05=08=A3=D0=A6=05=08=E9H=FD=FF=FF=C7=04$=01=00=00=00=E8=D4= +=EF=FF=FF=85=C0=0F=95=C0=0F=B6=C0=8DP=01=89=15=A0=A6=05=08=EB=CD1=C0=A3=A0= +=A6=05=081=C0=A3=88=A2=05=08=E9=16=FD=FF=FF=B8p=00=00=00=A3=BC=A6=05=08=B8= +=01=00=00=00=BB=01=00=00=00=A3=C0=A6=05=081=C0=A3=C4=A6=05=08=89=1D=90=A2= +=05=081=C9=89=0D=94=A2=05=08=E9=E3=FC=FF=FF=B8=01=00=00=00=A3=DC=A6=05=08= +=E9=D4=FC=FF=FF1=C0=BB=00=04=00=001=C9=A3=BC=A6=05=08=BA=00=04=00=00=89=1D= +=C0=A6=05=08=89=0D=C4=A6=05=08=89=15=90=A2=05=08=EB=BD1=C0=E9=82=FD=FF=FF= +=B8=04=00=00=00=E9x=FD=FF=FF=B8=01=00=00=00=A3=B4=A6=05=08=EB=E31=C0=A3=A0= +=A6=05=08=E9=C2=FD=FF=FF=B8=02=00=00=00=E9=AE=FD=FF=FF=B8=01=00=00=00=A3=F8= +=A6=05=08=E9k=FC=FF=FF=BB=01=00=00=00=89=1D=AC=A6=05=08=E9[=FC=FF=FF=B9=01= +=00=00=00=89=0D=B8=A6=05=08=E9K=FC=FF=FF=BA=03=00=00=00=89=15=A8=A6=05=08= +=E9=E6=FD=FF=FF=B8=02=00=00=00=E9=8F=FE=FF=FF=B8=05=00=00=00=E9=CD=FD=FF=FF= +1=C0=89D$=10=8DE=D8=89D$=0C1=C0=89D$=081=C0=89D$=04=A1h=A6=05=08=89=04$=E8= +=E1=99=00=00=85=C0u=11=8BE=D8=85=C0~=0A=A3=10=A7=05=08=E9=EC=FB=FF=FF=A1h= +=A6=05=08=89=04$=E8o=93=00=00=C7=04$=00=00=00=00=89=C3=B9=DF\=05=08=89L$=04= +=B8=05=00=00=00=89D$=08=E8=C7=EA=FF=FF=89D$=081=D2=89\$=0C=89T$=04=C7=04$= +=01=00=00=00=E8=ED=EE=FF=FF=8BE=D8=EB=AA=B8=03=00=00=00=E9r=FC=FF=FF=8D=B6= +=00=00=00=00=C7=04$=01=00=00=00=E9D=FC=FF=FF=C6=05=B0=A6=05=08=01=E9x=FB=FF= +=FF=B8=C0=A6=05=08=89D$=08=B8=01=00=00=00=89D$=04=A1h=A6=05=08=89=04$=E8Y= +=81=00=00=A3=BC=A6=05=08=8B=15=C4=A6=05=08=A1=C0=A6=05=08=89=15=94=A2=05=08= +=A3=90=A2=05=08=E99=FB=FF=FF=8B=15h=A6=05=08=B8=01=00=00=00=85=D2u81=DB=83= +=F8=01t*=83=F8=02t=15=89=1D=D0=A6=05=08=85=DB=0F=84=10=FB=FF=FF1=C0=E9=F1= +=FC=FF=FF=C7=04$=01=00=00=00=E8=95=ED=FF=FF=85=C0t=DB=BB=01=00=00=00=EB=D4= +=89T$=04=A1,=A3=05=08=B9=04=00=00=00=89L$=10=C7=04$=F6\=05=08=89D$=14=B8=80= +b=05=08=89D$=0C=B8@b=05=08=89D$=08=E8=DEp=00=00=8B=04=85=80b=05=08=EB=8B=BA= +=04=00=00=00=89=15=E0=A6=05=08=E9=A5=FA=FF=FF=C7=04$=FE\=05=08=A1,=A3=05=08= +=89D$=14=B8=04=00=00=00=89D$=10=B8=C0a=05=08=89D$=0C=B8=A0a=05=08=89D$=08= +=A1h=A6=05=08=89D$=04=E8=8Cp=00=00=8B=14=85=C0a=05=08=89=15=A0=A6=05=08=E9= +Z=FA=FF=FF=C7E=D0=08Z=05=081=DB=89=1D=A0=A6=05=08=E9F=FA=FF=FF=C7=04$=07]= +=05=08=A1,=A3=05=08=89D$=14=B8=04=00=00=00=89D$=10=B8=B8^=05=08=89D$=0C=B8= +=A8^=05=08=89D$=08=A1h=A6=05=08=89D$=04=E8-p=00=00=8B=04=85=B8^=05=08=E95= +=FB=FF=FF=C7=04$=19]=05=08=A1,=A3=05=08=B9=C0=8B=05=08=89L$=0C=BA=A0=8B=05= +=08=BB=04=00=00=00=89\$=10=89D$=14=A1h=A6=05=08=89T$=08=89D$=04=E8=E8o=00= +=00=8B=04=85=C0=8B=05=08=E92=FB=FF=FF1=C0=E9@=FD=FF=FF=B8P=00=00=00=E9=9A= +=FC=FF=FF=C7=04$)]=05=08=A1,=A3=05=08=B9=F4a=05=08=89L$=0C=BA=DCa=05=08=BB= +=04=00=00=00=89\$=10=89D$=14=A1h=A6=05=08=89T$=08=89D$=04=E8=92o=00=00=8B= +=04=85=F4a=05=08=E9=0C=FB=FF=FF=C7=04$0]=05=08=A1,=A3=05=08=89D$=14=B8=04= +=00=00=00=89D$=10=B8 b=05=08=89D$=0C=B8=08b=05=08=89D$=08=A1h=A6=05=08=89= +D$=04=E8Mo=00=00=8B=04=85 b=05=08=E9=7F=FB=FF=FF=A1h=A6=05=08=89E=D0=E9=14= +=F9=FF=FF=C7=04$=00=00=00=00=E8=08=87=00=00=A3=FC=A6=05=08=89=04$=E8{=87=00= +=00=83=F8=04=0F=84=A3=02=00=00=A1=CC=A6=05=08=85=C0t>=8D=986]=05=08=0F=B6= +=806]=05=08=84=C0t-=90=8D=B4&=00=00=00=00=0F=BE=C0C=BE=01=00=00=00=89D$=04= +=A1=FC=A6=05=08=89t$=08=89=04$=E8r=87=00=00=0F=B6=03=84=C0u=DB=C7=04$=00=00= +=00=00=E8=9F=86=00=00=A3=00=A7=05=08=B9=01=00=00=00=BA:=00=00=00=89L$=08=89= +T$=04=89=04$=E8@=87=00=00=A1=C8=A6=05=08=85=C0=0F=84=19=02=00=00=8B=15=A0= +=A6=05=08=85=D2t=071=C0=A3=C8=A6=05=08=A1=A4=A6=05=08H=83=F8=01w=15=8BE=CC= +=85=C0u=0E=85=D2t=0E=B8=03=00=00=00=A3=A8=A6=05=08=85=D2uA=8B]=D0=85=DB=0F= +=84=B9=01=00=00=8Dv=00=89=1C$=BF=06=00=00=00=BE=BCb=05=08=89|$=08=89t$=04= +=E8=DE=E8=FF=FF=85=C0u"=C7=04$=02=00=00=00=E8fr=00=00=85=C0t=05=83=C3=06=EB= +=CD=A1l=A6=05=08=83=C4L[^_]=C3=80;+=0F=84=DB=00=00=00=89\$=04=A1,=A3=05=08= +=B9=04=00=00=00=89L$=10=BA=94^=05=08=89T$=0C=89D$=14=B8=80^=05=08=89D$=08= +=C7=04$<]=05=08=E8=D3m=00=00=8B=04=85=94^=05=08=83=F8=01=0F=84=86=00=00=00= +=83=F8=01rh=83=F8=02tI=83=F8=03u=97=C7=04$=02=00=00=00=E8=E8q=00=00=85=C0= +t=871=DB=C7=04$=00=00=00=00=8B=04=9D=18=A3=05=08=BE=02=00=00=00=89t$=08=89= +D$=04=E8j=E6=FF=FF=89=04=9D=18=A3=05=08C=83=FB=01v=D3=E9S=FF=FF=FF=B8G]=05= +=08=BFl]=05=08=A3=18=A3=05=08=89=3D=1C=A3=05=08=E99=FF=FF=FF=B8Q]=05=08=A3= +=1C=A3=05=08=B8Q]=05=08=A3=18=A3=05=08=E9 =FF=FF=FF=B8i]=05=08=A3=1C=A3=05= +=08=B8i]=05=08=EB=E5=B9=0A=00=00=00=8D{=01=89L$=04=89<$=E8`=E5=FF=FF=85=C0= +=89=C6tt=8D@=01=BA=0A=00=00=00=89E=C8=89T$=04=89=04$=E8C=E5=FF=FF=85=C0tB= +=89<$=E8=AF=84=00=00=C7=04$=00=00=00=00=89=C3=B8=05=00=00=00=89D$=08=B8x]= +=05=08=89D$=04=E8=B7=E5=FF=FF=89\$=0C1=DB=89D$=08=89\$=04=C7=04$=01=00=00= +=00=E8=DD=E9=FF=FF=C6=06=00=8Bu=C8=89=3D=18=A3=05=08=895=1C=A3=05=08=E9=81= +=FE=FF=FF=89=FE=EB=EB=C7=04$=95]=05=08=E8f=E5=FF=FF=85=C0=89=C3=0F=854=FE= +=FF=FF=BB=A0]=05=08=E9*=FE=FF=FF=8B=15=A0=A6=05=08=E9=ED=FD=FF=FF=B8=01=00= +=00=00=BF =00=00=00=89D$=08=A1=FC=A6=05=08=89|$=04=89=04$=E8=F0=84=00=00=E9= +9=FD=FF=FF=C7=04$=AF]=05=08=E8=17=E5=FF=FF=85=C0=89=C3=0F=84=15=F6=FF=FF=89= +=1C$1=C0=89D$=10=8DE=DC=89D$=0C1=C0=89D$=081=C0=89D$=04=E8=D4=93=00=00=85= +=C0u=0B=8BE=DC=85=C0=0F=89=CD=F7=FF=FF=89=1C$=E8m=8D=00=00=C7=04$=00=00=00= +=00=89=C3=B8=05=00=00=00=89D$=08=B8=00l=05=08=89D$=04=E8=C5=E4=FF=FF=89D$= +=081=C9=89\$=0C=89L$=04=C7=04$=00=00=00=00=E8=EB=E8=FF=FF=E9=9E=F5=FF=FF=89= +=1C$1=C0=89D$=10=8DE=EC=89D$=0C1=C0=89D$=081=C0=89D$=04=E8]=93=00=00=85=C0= +u=11=8BE=EC=85=C0~=0A=A3=10=A7=05=08=E9=0C=F5=FF=FF=89=1C$=E8=F0=8C=00=00= +=C7=04$=00=00=00=00=89=C3=B8=05=00=00=00=89D$=08=B8@l=05=08=89D$=04=E8H=E4= +=FF=FF=89D$=081=C9=89\$=0C=89L$=04=C7=04$=00=00=00=00=E8n=E8=FF=FF=E9=C5=F4= +=FF=FF=C7=04$=97\=05=08=E8=0D=E4=FF=FF=85=C0=0F=84=8C=F4=FF=FF=E9q=F4=FF=FF= +=89=1C$=E8=90=8C=00=00=C7=04$=00=00=00=00=89=C3=B8=05=00=00=00=89D$=08=B8= +=80l=05=08=89D$=04=E8=E8=E3=FF=FF=89\$=0C1=DB=89D$=08=89\$=04=C7=04$=00=00= +=00=00=E8=0E=E8=FF=FF=E9=F8=F3=FF=FF=B8=01=00=00=00=A3=A0=A6=05=081=C0=E9= +'=F3=FF=FF=83=F8=03=0F=85=F6=F2=FF=FF1=C0=A3=A0=A6=05=08=C7=04$=00=00=00=00= +=B8=04=00=00=00=89D$=04=E8+=83=00=00=E9=02=F3=FF=FF=BA=02=00=00=00=89=15=A0= +=A6=05=08=EB=D9=89=F6=8D=BC'=00=00=00=00U1=D2=89=E5WVS=83=EC=04=8BE=0C=C7= +E=F0=00=00=00=00=8B=18=8BE=08=8B0=8Dt&=001=FF=8D=B4&=00=00=00=00=8D=BC'=00= +=00=00=00=FF$=BD=04g=05=08=0F=B6=0B=0F=BE=C1=83=F8=3Dtg=83=F8=3D=7FI=85=C0= +t9=83=F8:t4=88=0ECF=FFE=F0=89=F6=8D=BC'=00=00=00=00=83=FF=04v=CB=8BE=08=83= +=FF=06=890=8BE=0C=89=18=8BE=F0t=06[[^_]=C3=B8=FF=FF=FF=FF=EB=F3=BF=05=00=00= +=00=EB=DA=90=8Dt&=00=83=F8\t=0D=83=F8^u=B6=BF=04=00=00=00C=EB=8E=BF=01=00= +=00=00=EB=F6=8BE=10=85=C0t=A0=EB=D2=0F=B6=03=0F=BE=C8=83=F9xws=FF$=8D=18g= +=05=08=0F=BE=C0=BF=02=00=00=00=8DP=D0=90C=EB=8D=BF=06=00=00=00=EB=F6=BA=7F= +=00=00=00=90=83=FF=01u=EB=88=161=FFF=FFE=F0=EB=E1=BA =00=00=00=EB=EA=BA=07= +=00=00=00=EB=E3=BA=08=00=00=00=EB=DC=BA=1B=00=00=00=EB=D5=BA=0C=00=00=00=EB= +=CE=BA=0A=00=00=00=EB=C7=BA=0D=00=00=00=EB=C0=BA=09=00=00=00=EB=B9=BA=0B=00= +=00=00=EB=B2=89=F6=0F=BE=D0=EB=AB=BF=03=00=00=001=D2=EB=92=89=F6=0F=B6=0B= +=88=C8,0<=07w=0C=0F=BE=C1=8DT=D0=D0=E9y=FF=FF=FF=88=16F=FFE=F0=E9=BE=FE=FF= +=FF=0F=B6=0B=0F=BE=C1=83=E80=83=F86w=E7=FF$=85=FCh=05=08=C1=E2=04=0F=BE=C1= +=8DT=10=D0=E9J=FF=FF=FF=C1=E2=04=0F=BE=C1=8DT=10=C9=E9;=FF=FF=FF=C1=E2=04= +=0F=BE=C1=8DT=10=A9=E9,=FF=FF=FF=0F=B6=031=FF=8D=B4&=00=00=00=00=8D=BC'=00=00=00=00=89T$=04=8B= +E=08=89D$=0C1=C0=89D$=08=8DC=13=89=04$=E8$=06=00=00=01=85=F0=FC=FF=FF=11=95= +=F4=FC=FF=FF=EB=86=8D=B6=00=00=00=00=0F=B6=D0=EB=CB=8B=1E=85=DB=0F=85=CF=03= +=00=00=85=FFt=0C=89<$=E8M=DF=FF=FF=85=C0tV=8BU=08=89=14$=E86=87=00=00=C7=04= +$=00=00=00=00=B9=05=00=00=00=BA=E0]=05=08=89L$=08=89=C6=89T$=04=E8=8E=DC=FF= +=FF=89=C3=E8=E7=DC=FF=FF=89t$=0C=89\$=08=8B=00=C7=04$=00=00=00=00=89D$=04= +=E8=AD=E0=FF=FF=B8=01=00=00=00=A3=1C=A7=05=08=E86=15=00=00=A1=E4=A6=05=08= +=85=C0=0F=851=03=00=00=A1=0C=A7=05=08=85=C0=0F=84=DA=00=00=00=A1 =A3=05=08= +=85=C0u=1D=A1`=A6=05=08=8BP=14;P=18=0F=83=F4=02=00=00=C6=02=0A=FF@=14=FF=05= +(=A7=05=08=8B=1D=C8=A6=05=081=F6=895 =A3=05=08=85=DB=0F=85C=02=00=001=C0=89= +D$=0C=A1=00=A7=05=08=89D$=08=8BE=0C=85=C0=0F=84!=02=00=00=89D$=04=A1`=A6=05= +=08=89=04$=E8=F5=1F=00=00=01=05(=A7=05=08=A1=C8=A6=05=08=85=C0t6=8B=15=8C= +=A7=05=08=8DB=04;=05=90=A7=05=08=0F=87=CC=01=00=00=89=14$=BE=04=00=00=00=BB= +(=A7=05=08=89t$=08=89\$=04=E8=CF=DD=FF=FF=83=05=8C=A7=05=08=04=C7=04$=F5]= +=05=08=A1`=A6=05=08=B9=02=00=00=00=89L$=08=BA=01=00=00=00=89T$=04=89D$=0C= +=E8=91=DB=FF=FF=83=05(=A7=05=08=02=A1=A0=A6=05=08=85=C0t=0D=A1=B8=A6=05=08= +=85=C0=0F=84=F0=00=00=00=A1=C8=A6=05=08=85=C0=0F=85'=01=00=00=C7=04$=00=00= +=00=00=B8=05=00=00=00=BF=F8]=05=08=89D$=08=89|$=04=E8)=DB=FF=FF=89=C3=A1`= +=A6=05=08=89=1C$=89D$=04=E8=D6=DA=FF=FF=89=1C$=E8>=DC=FF=FF=01=05(=A7=05=08= +=A1`=A6=05=08=8BP=14;P=18=0F=83=C1=00=00=00=C6=02 =FF@=14=FF=05(=A7=05=08= +1=C9=A1=C0=A6=05=08=89L$=14=8B=15=C4=A6=05=08=BB=00=02=00=00=89\$=10=89D$= +=18=A1=BC=A6=05=08=89T$=1C=8B=95=F4=FC=FF=FF=89D$=0C=8D=85=F8=FC=FF=FF=89= +D$=08=8B=85=F0=FC=FF=FF=89T$=04=89=04$=E8=A6g=00=00=89=C3=A1`=A6=05=08=89= +=1C$=89D$=04=E8K=DA=FF=FF=89=1C$=E8=B3=DB=FF=FF=01=05(=A7=05=08=A1`=A6=05= +=08=8BP=14;P=18s'=C6=02=0A=FF@=14=FF=05(=A7=05=08=A1=90=A6=05=08=85=C0u=0B= +=81=C4,=03=00=00[^_]=C3=E8c=15=00=00=EB=EE=89=04$=BA=0A=00=00=00=89T$=04=E8= +=A8=D9=FF=FF=EB=CC=89=04$=BE =00=00=00=89t$=04=E8=95=D9=FF=FF=E9/=FF=FF=FF= +=C7=04$=FE]=05=08=A1`=A6=05=08=89D$=0C=B8=02=00=00=00=89D$=08=B8=01=00=00= +=00=89D$=04=E8=19=DA=FF=FF=83=05(=A7=05=08=02=E9=A6=FE=FF=FF=C7=04$=80=A7= +=05=08=BF=04=00=00=00=89|$=04=E8X=DD=FF=FF=8B=15=8C=A7=05=08=E9=14=FE=FF=FF= +=8BE=08=E9=D7=FD=FF=FF=C7=04$=FE]=05=08=A1`=A6=05=08=B9=02=00=00=00=89L$=08= +=BA=01=00=00=00=89T$=04=89D$=0C=E8=BE=D9=FF=FF=83=05(=A7=05=08=02=A1=C8=A6= +=05=08=85=C0=0F=84=82=FD=FF=FF=8B=15=8C=A7=05=08=8DB=04;=05=90=A7=05=08w&= +=89=14$=B8=04=00=00=00=89D$=08=B8(=A7=05=08=89D$=04=E8=8F=DB=FF=FF=83=05=8C= +=A7=05=08=04=E9K=FD=FF=FF=C7=04$=80=A7=05=08=B8=04=00=00=00=89D$=04=E8=BE= +=DC=FF=FF=8B=15=8C=A7=05=08=EB=BD=89=04$=BF=0A=00=00=00=89|$=04=E8=95=D8=FF= +=FF=E9=FC=FC=FF=FF=B8=01=00=00=00=89D$=04=8BE=08=89=04$=E8t=09=00=00=A1=E4= +=A6=05=08=85=C0=0F=85=BB=FC=FF=FF=E9=A9=FC=FF=FF=89<$=E8=82=DB=FF=FF=89=1E= +=E92=FC=FF=FF=C7=04$=C0=A7=05=08=B8=10=00=00=00=89D$=04=E8V=DC=FF=FF=8B=15= +=CC=A7=05=08=E9>=FB=FF=FF=8BE=08=89=04$=E8H=83=00=00=C7=04$=00=00=00=00=89= +=C3=B8=05=00=00=00=89D$=08=B8 m=05=08=89D$=04=E8=A0=D8=FF=FF=89D$=081=C0=89= +\$=0C=89D$=04=C7=04$=00=00=00=00=E8=C6=DC=FF=FF=E9&=FE=FF=FF=8BU=08=89=14= +$=E8=FE=82=00=00=C7=04$=00=00=00=00=B9=05=00=00=00=BA`m=05=08=89L$=08=89=C6= +=89T$=04=E8V=D8=FF=FF=89=C3=E8=AF=D8=FF=FF=89t$=0C=89\$=08=8B=00=89D$=04=C7= +=04$=00=00=00=00=E8u=DC=FF=FF=B8=01=00=00=00=A3=1C=A7=05=08=E9=CB=FD=FF=FF= +=C7=04$=03=00=00=00=8DE=88=89D$=08=8BE=08=89D$=04=E8=DC=D9=FF=FF=E94=FA=FF= +=FF=8BU=08=BEN=87=05=08=89=14$=E8=7F=82=00=00=89D$=0C=89t$=08=8B=03=EB=A6= +=8Dv=00U=89=E5=83=EC=08=C7=04$=08=00=00=00=E8=DE=84=00=00=8BU=08=89=10=8B= +=15=F4=A6=05=08=89P=04=A3=F4=A6=05=08=89=EC]=C3=8D=B4&=00=00=00=00U=89=E5= +WVS=83=EC=0C=8B=1D=F4=A6=05=08=8B}=08=85=DBt.=8Dw=13=8D=B4&=00=00=00=00=89= +t$=04=B8=04=00=00=00=89D$=08=8B=03=89=04$=E8=81=D8=FF=FF1=D2=85=C0t=1B=8B= +[=04=85=DBu=DC=A1=F0=A6=05=08=85=C0u=06=80=7F=13.t=0F=BA=01=00=00=00=83=C4= +=0C=89=D0[^_]=C3=A1=EC=A6=05=08=85=C0t=0D=80=7F=14=00t=07f=83=7F=14.u=DB1= +=D2=EB=DC=8Dt&=00U=89=E5V1=F6S=83=EC=10;5=90=A6=05=08}71=DB=8D=B6=00=00=00= +=00=8D=BF=00=00=00=00=A1=88=A6=05=08=8B=04=18=89=04$=E8h=DA=FF=FF=A1=88=A6= +=05=08=8BD=18d=85=C0u$F=83=C3x;5=90=A6=05=08|=D71=C0=A3=90=A6=05=08=B8=04= +=00=00=00=A3=9C=A6=05=08=83=C4=10[^]=C3=89=04$=E8/=DA=FF=FF=EB=D2=90=8Dt&= +=00U=89=E5WVS=81=EC,=03=00=00=8B=0D=90=A6=05=08;=0D=8C=A6=05=08=8B]=0C=0F= +=84E=05=00=00=89=C8=8B5=88=A6=05=081=D2=C1=E0=04)=C8=C1=E0=03=89T=06d1=C9= +=89=F7=89L=06h1=D2=89T=06l=8BE=10=85=C0=0F=85=B0=00=00=00=A1=14=A7=05=08=85= +=C0=0F=85=A3=00=00=00=A1=18=A7=05=08=85=C0t=1A=85=DB=0F=84=92=00=00=00=83= +=FB=0A=0F=84=89=00=00=00=83=FB=08tk=8Dt&=00=8B=15=90=A6=05=081=C9=89=8D=F0= +=FC=FF=FF=89=D0=C1=E0=04)=D0=C1=E0=031=D2=89\=07p=C1=E3=0C=89\=07=14=89=95= +=F4=FC=FF=FF=90=8Dt&=00=A1=90=A6=05=08=8BU=08=89=C3=89=14$=C1=E3=04)=C3=C1= +=E3=03=E8=06=84=00=00=89=04=1E=8B=95=F4=FC=FF=FF=8B=85=F0=FC=FF=FF=FF=05=90= +=A6=05=08=8De=F4[^_]=C3=83=3D=CC=A6=05=08=01t=10=A1=D0=A6=05=08=85=C0t=87= +=8D=B4&=00=00=00=00=8BE=08=808/t=0C=8BU=14=80:=00=0F=85=0C=04=00=00=8B}=08= +=A1=E0=A6=05=08=83=F8=03r=12=83=F8=04=0F=86l=03=00=00=83=F8=05=0F=849=03=00= +=00=89|$=04=8B=15=90=A6=05=08=C7=04$=03=00=00=00=89=D0=C1=E0=04)=D0=8DD=C6= +=04=89D$=08=E8P=D8=FF=FF=89=C3=85=DB=0F=88=C8=02=00=00=8B=0D=A0=A6=05=08=85= +=C9=0F=84B=02=00=00=8B=0D=90=A6=05=08=8B5=88=A6=05=08=89=C8=C1=E0=04)=C8=8D= +=14=C5=00=00=00=00=8BD=16=14%=00=F0=00=00=3D=00=A0=00=00=0F=84=12=01=00=00= +=89=C8=C1=E0=04)=C8=C1=E0=03=8BT=06=14=81=E2=00=F0=00=00=81=FA=00=A0=00=00= +=0F=84=E7=00=00=00=81=FA=00@=00=00=0F=84=9B=00=00=00=BF=08=00=00=00=89|=06= +p=89=C81=DB=C1=E0=04)=C8=8BT=C6<=8BL=C6@=89\$=14=A1=C0=A6=05=08=BE=00=02=00= +=00=89=95=F0=FC=FF=FF=8B=15=C4=A6=05=08=89D$=18=A1=BC=A6=05=08=89=8D=F4=FC= +=FF=FF=89T$=1C=8B=95=F4=FC=FF=FF=89D$=0C=8D=85=F8=FC=FF=FF=89D$=08=8B=85=F0= +=FC=FF=FF=89T$=04=89t$=10=89=04$=E8=95a=00=00=89=04$=E8=B5=D5=FF=FF9=05=9C= +=A6=05=08}=0F=83=F8=07~=05=B8=07=00=00=00=A3=9C=A6=05=08=8B5=88=A6=05=08=E9= +K=FE=FF=FF=8BU=10=85=D2t$=8B=3D=E8=A6=05=08=85=FFu=1A=BB=1E=00=00=00=90=8D= +t&=00=89\=06p=E9J=FF=FF=FF=8D=B4&=00=00=00=00=89=C8=BA=04=00=00=00=C1=E0=04= +)=C8=89T=C6p=E9.=FF=FF=FF=BB=0A=00=00=00=EB=D4=8Dt&=00=A1=A0=A6=05=08=85=C0= +t=0D=A1=D8=A6=05=08=85=C0=0F=84=D8=FE=FF=FF=89<$=8D=042=89D$=04=E8=CB=02=00= +=00=8B=15=90=A6=05=08=89=D0=C1=E0=04)=D0=8B=15=88=A6=05=08=8BD=C2d=89<$=89= +D$=04=E88=03=00=00=85=C0=89=C3=0F=84=9A=00=00=00=8B5=CC=A6=05=08=85=F6u=0E= +=8B=0D=D8=A6=05=08=85=C9=0F=84v=00=00=00=89\$=04=8DE=88=89D$=08=C7=04$=03= +=00=00=00=E8g=D5=FF=FF=85=C0u[=8B=15=90=A6=05=08=8B5=88=A6=05=08=89=D0=C1= +=E0=04)=D0=BA=01=00=00=00=89T=C6l=8BE=10=85=C0tU=A1=A0=A6=05=08=85=C0tL=8B= +M=98=89=C8%=00=F0=00=00=3D=00@=00=00t=1E=8B=15=90=A6=05=08=BF=01=00=00=00= +=89=D0=C1=E0=04)=D0=C1=E0=03=89L=06h=89|=06l=90=85=DBt=08=89=1C$=E8=8C=D6= +=FF=FF=8B=0D=90=A6=05=08=8B5=88=A6=05=08=E9=F1=FD=FF=FF=8BM=98=EB=C0=89<$= +=8B=15=90=A6=05=08=89=D0=C1=E0=04)=D0=8B=15=88=A6=05=08=8DD=C2=04=89D$=04= +=E8=0B-=00=00=8B=0D=90=A6=05=08=8B5=88=A6=05=08=89=CA=C1=E2=04)=CA=85=C0=0F= +=9FD=D6t=85=C0=0F=89=85=FD=FF=FF=89<$=E8Q}=00=00=89=C3=E8"=D3=FF=FF=89\$=0C= +=BAN=87=05=08=89T$=08=8B=00=C7=04$=00=00=00=00=89D$=04=E8=E3=D6=FF=FF=E9F= +=FD=FF=FF=89<$=BEN=87=05=08=E8=19}=00=00=89=C3=E8=EA=D2=FF=FF=89\$=0C=BB=01= +=00=00=00=89t$=08=8B=00=C7=04$=00=00=00=00=89D$=04=E8=AB=D6=FF=FF=89=1D=1C= +=A7=05=081=C01=D2=E9s=FC=FF=FF=89|$=04=8B=15=90=A6=05=08=C7=04$=03=00=00=00= +=89=D0=C1=E0=04)=D0=8DD=C6=04=89D$=08=E8=07=D4=FF=FF=E9=C2=FC=FF=FF=8BE=10= +=85=C0=0F=84=92=FC=FF=FF=89|$=04=8B=15=90=A6=05=08=C7=04$=03=00=00=00=89=D0= +=C1=E0=04)=D0=8DD=C6=04=89D$=08=E8=D2=D3=FF=FF=83=3D=E0=A6=05=08=03=89=C3= +=0F=84=85=FC=FF=FF=85=C0x:=8B=0D=90=A6=05=08=8B5=88=A6=05=08=89=C8=C1=E0=04= +)=C8=8BD=C6=14%=00=F0=00=00=3D=00@=00=00=0F=95=C0=0F=B6=C0=85=C0=0F=84R=FC= +=FF=FF=8B5=88=A6=05=08=E9 =FC=FF=FF=E8=10=D2=FF=FF=838=02=0F=94=C0=EB=DD=8B= +M=08=89=0C$=E8=CD=D2=FF=FF=89=C3=8BE=14=89=04$=E8=C0=D2=FF=FF=8DD=18=11=8B= +U=08=83=E0=F0=8BM=14)=C4=8D|$ =89T$=08=89L$=04=89<$=E8=95%=00=00=8B5=88=A6= +=05=08=E9=B1=FB=FF=FF=8D=04=09=C1=E1=05)=C1=A3=8C=A6=05=08=8D=04=CD=00=00= +=00=00=89D$=04=A1=88=A6=05=08=89=04$=E8e~=00=00=A3=88=A6=05=08=8B=0D=90=A6= +=05=08=E9=86=FA=FF=FF=90=8Dt&=00U=89=E5=83=EC=18=89]=F8=8B]=08=89u=FC=89=1C= +$=E8=99~=00=00=8BU=0C=85=C0=89Bdt=0F=8B]=F8=8Bu=FC=89=EC]=C3=90=8Dt&=00=89= +=1C$=BB=01^=05=08=E8s{=00=00=89\$=04=89=C6=B8=05=00=00=00=89D$=08=C7=04$=00= +=00=00=00=E8=D0=D0=FF=FF=89=C3=E8)=D1=FF=FF=89t$=0C=89\$=08=8B=00=C7=04$=00= +=00=00=00=89D$=04=E8=EF=D4=FF=FF=B9=01=00=00=00=89=0D=1C=A7=05=08=EB=9B=8D= +v=00=8D=BC'=00=00=00=00U=89=E5=83=EC(=89}=FC=8BE=08=8B}=0C=89]=F4=89E=F01= +=C0=85=FF=89u=F8ta=80?/tm=B8/=00=00=00=89D$=04=8BE=F0=89=04$=E8=C1=D2=FF=FF= +=85=C0=89=C6tS=8BE=F0=89<$)=C6=8D^=01=E8{=D1=FF=FF=8DD=18=01=89=04$=E8G}=00= +=00=89\$=08=89=C6=8BE=F0=894$=8D=1C3=89D$=04=E8=17=D4=FF=FF=89|$=04=89=1C= +$=E8=8B=D4=FF=FF=89=F0=8B]=F4=8Bu=F8=8B}=FC=89=EC]=C3=8Dt&=00=89}=08=8B]=F4= +=8Bu=F8=8B}=FC=89=EC]=E9=FC}=00=00=8D=B6=00=00=00=00=8D=BF=00=00=00=00U=89= +=E5=83=EC=08=8BE=08=89=04$=E8=DFW=00=001=C9=808.t=08=89=EC=89=C8]=C3=89=F6= +=0F=B6P=01=84=D2t=18=80=FA.u=EB=80x=02=00u=E5=8D=B6=00=00=00=00=8D=BC'=00= +=00=00=00=B9=01=00=00=00=EB=D1=89=F6=8D=BC'=00=00=00=00U=89=E5WVS=83=EC=1C= +=8BE=08=808=00t=0E=8B=15=84=A6=05=08=85=D2=0F=85_=01=00=00=A1=90=A6=05=08= +=89=C3Kx:=89=D8=C1=E0=04)=D8=8D4=C5=00=00=00=00=89=F6=8D=BC'=00=00=00=00=8B= +=15=88=A6=05=08=8BD2p=83=F8=04=0F=84}=00=00=00=83=F8=1Etx=83=EExKy=E2=A1=90= +=A6=05=08=C7E=F0=00=00=00=001=DB9=C3}O=FC=8B=15=88=A6=05=08=C7E=EC=00=00=00= +=00=C7E=E8=00=00=00=00=8Dt&=00=8D=BC'=00=00=00=00=8BM=EC=83|=0Ap=1Et=1C=FF= +E=F0=89=CE=8B}=E8=83E=E8x=01=D6=B9=1E=00=00=00=01=D7=F3=A5=A1=90=A6=05=08= +=83E=ECxC9=C3|=D1=8BE=F0=A3=90=A6=05=08=83=C4=1C[^_]=C3=90=8BE=0C=85=C0=0F= +=85=85=00=00=00=8B=042=808/t=08=8BM=08=809=00u5=8BD2d=89D$=04=8B=042=89=04= +$=E8=F2=EF=FF=FF=8B=15=88=A6=05=08=83|2p=1E=0F=85I=FF=FF=FF=8B=042=89=04$= +=E8=DE=D1=FF=FF=E99=FF=FF=FF=901=C0=89D$=08=8B=042=89D$=04=8BE=08=89=04$=E8= +=08l=00=00=89=C7=A1=88=A6=05=08=8BD0d=89<$=89D$=04=E8=A1=EF=FF=FF=89<$=E8= +=A1=D1=FF=FF=EB=A5=8D=B4&=00=00=00=00=8B=042=89=04$=E8E=FE=FF=FF=85=C0=0F= +=85=E5=FE=FF=FF=8B=15=88=A6=05=08=E9]=FF=FF=FF=89D$=04=C7=04$=00=00=00=00= +=E8b=EF=FF=FF=E9=8C=FE=FF=FF=8D=B6=00=00=00=00=8D=BC'=00=00=00=00U=89=E5=83= +=EC=18=E8M=CE=FF=FF=C7=00=00=00=00=00=8BE=0C=89D$=04=8BE=08=89=04$=E8=95=D0= +=FF=FF=89=EC]=C3=89=F6=8D=BC'=00=00=00=00U=B8=FF=FF=FF=FF=89=E5=83=EC=18=89= +]=F8=8B]=0C=89u=FC=8Bu=08=8BKT=8BVT9=D1|=1F=B8=01=00=00=00=7F=18=8BKX=B8=FF= +=FF=FF=FF=8BVX9=D1|=09=0F=9F=C0=0F=B6=C0=8Dv=00=85=C0t=0C=8B]=F8=8Bu=FC=89= +=EC]=C3=89=F6=8B=03=89D$=04=8B=06=89=04$=E8p=FF=FF=FF=EB=E2=8D=B4&=00=00=00= +=00=8D=BC'=00=00=00=00U=B8=FF=FF=FF=FF=89=E5=83=EC=18=89]=F8=8B]=0C=89u=FC= +=8Bu=08=8BKT=8BVT9=D1|=1F=B8=01=00=00=00=7F=18=8BKX=B8=FF=FF=FF=FF=8BVX9=D1= +|=09=0F=9F=C0=0F=B6=C0=8Dv=00=85=C0t=0C=8B]=F8=8Bu=FC=89=EC]=C3=89=F6=8B=03= +=89D$=04=8B=06=89=04$=E8=C8=CC=FF=FF=EB=E2=8D=B4&=00=00=00=00=8D=BC'=00=00= +=00=00U=89=E5=8BU=0C=8BE=08=89U=08=89E=0C]=E9=0B=FF=FF=FF=8Dt&=00=8D=BC'=00= +=00=00=00U=89=E5=8BU=0C=8BE=08=89U=08=89E=0C]=E9[=FF=FF=FF=8Dt&=00=8D=BC'= +=00=00=00=00U=B8=FF=FF=FF=FF=89=E5=83=EC=18=89]=F8=8B]=0C=89u=FC=8Bu=08=8B= +KL=8BVL9=D1|=1F=B8=01=00=00=00=7F=18=8BKP=B8=FF=FF=FF=FF=8BVP9=D1|=09=0F=9F= +=C0=0F=B6=C0=8Dv=00=85=C0t=0C=8B]=F8=8Bu=FC=89=EC]=C3=89=F6=8B=03=89D$=04= +=8B=06=89=04$=E8P=FE=FF=FF=EB=E2=8D=B4&=00=00=00=00=8D=BC'=00=00=00=00U=B8= +=FF=FF=FF=FF=89=E5=83=EC=18=89]=F8=8B]=0C=89u=FC=8Bu=08=8BKL=8BVL9=D1|=1F= +=B8=01=00=00=00=7F=18=8BKP=B8=FF=FF=FF=FF=8BVP9=D1|=09=0F=9F=C0=0F=B6=C0=8D= +v=00=85=C0t=0C=8B]=F8=8Bu=FC=89=EC]=C3=89=F6=8B=03=89D$=04=8B=06=89=04$=E8= +=A8=CB=FF=FF=EB=E2=8D=B4&=00=00=00=00=8D=BC'=00=00=00=00U=89=E5=8BU=0C=8B= +E=08=89U=08=89E=0C]=E9=0B=FF=FF=FF=8Dt&=00=8D=BC'=00=00=00=00U=89=E5=8BU=0C= +=8BE=08=89U=08=89E=0C]=E9[=FF=FF=FF=8Dt&=00=8D=BC'=00=00=00=00U=B8=FF=FF=FF= +=FF=89=E5=83=EC=18=89]=F8=8B]=0C=89u=FC=8Bu=08=8BKD=8BVD9=D1|=1F=B8=01=00= +=00=00=7F=18=8BKH=B8=FF=FF=FF=FF=8BVH9=D1|=09=0F=9F=C0=0F=B6=C0=8Dv=00=85= +=C0t=0C=8B]=F8=8Bu=FC=89=EC]=C3=89=F6=8B=03=89D$=04=8B=06=89=04$=E80=FD=FF= +=FF=EB=E2=8D=B4&=00=00=00=00=8D=BC'=00=00=00=00U=B8=FF=FF=FF=FF=89=E5=83=EC= +=18=89]=F8=8B]=0C=89u=FC=8Bu=08=8BKD=8BVD9=D1|=1F=B8=01=00=00=00=7F=18=8B= +KH=B8=FF=FF=FF=FF=8BVH9=D1|=09=0F=9F=C0=0F=B6=C0=8Dv=00=85=C0t=0C=8B]=F8=8B= +u=FC=89=EC]=C3=89=F6=8B=03=89D$=04=8B=06=89=04$=E8=88=CA=FF=FF=EB=E2=8D=B4= +&=00=00=00=00=8D=BC'=00=00=00=00U=89=E5=8BU=0C=8BE=08=89U=08=89E=0C]=E9=0B= +=FF=FF=FF=8Dt&=00=8D=BC'=00=00=00=00U=89=E5=8BU=0C=8BE=08=89U=08=89E=0C]=E9= +[=FF=FF=FF=8Dt&=00=8D=BC'=00=00=00=00U=89=E5=83=EC=18=89}=FC=8BE=08=8B}=0C= +=89]=F4=89u=F8=8Bw4=8BH4=8B_0=8BP09=CE|A=7F=049=D3r;9=CE=B8=01=00=00=00=7F= +=0C|=049=D3w=061=C0=8Dt&=00=85=C0t=0D=8B]=F4=8Bu=F8=8B}=FC=89=EC]=C3=8B=07= +=8BU=08=89D$=04=8B=02=89=04$=E8=0C=FC=FF=FF=EB=DE=B8=FF=FF=FF=FF=EB=D3=8D= +v=00U=89=E5=83=EC=18=89}=FC=8BE=08=8B}=0C=89]=F4=89u=F8=8Bw4=8BH4=8B_0=8B= +P09=CE|A=7F=049=D3r;9=CE=B8=01=00=00=00=7F=0C|=049=D3w=061=C0=8Dt&=00=85=C0= +t=0D=8B]=F4=8Bu=F8=8B}=FC=89=EC]=C3=8B=07=8BU=08=89D$=04=8B=02=89=04$=E8d= +=C9=FF=FF=EB=DE=B8=FF=FF=FF=FF=EB=D3=8Dv=00U=89=E5=8BU=0C=8BE=08=89U=08=89= +E=0C]=E9=0B=FF=FF=FF=8Dt&=00=8D=BC'=00=00=00=00U=89=E5=8BU=0C=8BE=08=89U=08= +=89E=0C]=E9[=FF=FF=FF=8Dt&=00=8D=BC'=00=00=00=00U=89=E5=8BE=0C=8B=00=89E=0C= +=8BE=08=8B=00=89E=08]=E9=CF=CA=FF=FF=8D=B4&=00=00=00=00U=89=E5=8BU=0C=8BE= +=08=89U=08=89E=0C]=EB=CE=8D=B4&=00=00=00=00=8D=BC'=00=00=00=00U=89=E5=83=EC= +=08=8BE=0C=8B=00=89D$=04=8BE=08=8B=00=89=04$=E8=F4=FA=FF=FF=89=EC]=C3U=89= +=E5=83=EC=08=8BE=0C=8B=00=89D$=04=8BE=08=8B=00=89=04$=E8=9C=C8=FF=FF=89=EC= +]=C3U=89=E5=8BU=0C=8BE=08=89U=08=89E=0C]=EB=AE=8D=B4&=00=00=00=00=8D=BC'=00= +=00=00=00U=89=E5=8BU=0C=8BE=08=89U=08=89E=0C]=EB=AE=8D=B4&=00=00=00=00=8D= +=BC'=00=00=00=00U=89=E5=83=EC=18=89}=FC=8B}=08=89]=F4=BB.=00=00=00=89u=F8= +=8Bu=0C=89\$=04=8B=07=89=04$=E8=C0=CA=FF=FF=B9.=00=00=00=89=C3=89L$=04=8B= +=06=89=04$=E8=AB=CA=FF=FF=85=C0t?=89D$=04=85=DB=89=D8t+=89=04$=E8=3D=FA=FF= +=FF=85=C0t=0D=8B]=F4=8Bu=F8=8B}=FC=89=EC]=C3=8B=06=89D$=04=8B=07=89=04$=E8= +=1C=FA=FF=FF=EB=E1=B8E\=05=08=EB=CE=8Dv=00=B8E\=05=08=EB=BA=89=F6=8D=BC'=00= +=00=00=00U=B8.=00=00=00=89=E5=83=EC=18=89}=FC=8B}=08=89]=F4=89u=F8=8Bu=0C= +=89D$=04=8B=07=89=04$=E80=CA=FF=FF=89=C3=B8.=00=00=00=89D$=04=8B=06=89=04= +$=E8=1B=CA=FF=FF=85=C0t?=89D$=04=85=DB=89=D8t+=89=04$=E8u=C7=FF=FF=85=C0t= +=0D=8B]=F4=8Bu=F8=8B}=FC=89=EC]=C3=8B=06=89D$=04=8B=07=89=04$=E8T=C7=FF=FF= +=EB=E1=B8E\=05=08=EB=CE=8Dv=00=B8E\=05=08=EB=BA=89=F6=8D=BC'=00=00=00=00U= +=89=E5=8BU=0C=8BE=08=89U=08=89E=0C]=E9=CB=FE=FF=FF=8Dt&=00=8D=BC'=00=00=00= +=00U=89=E5=8BU=0C=8BE=08=89U=08=89E=0C]=E9;=FF=FF=FF=8Dt&=00=8D=BC'=00=00= +=00=00U=89=E5=83=EC(=89]=F4=A1=A8=A6=05=08=89u=F8=89}=FC=83=F8=05w`=FF$=85= +=D8i=05=08=A1=A4=A6=05=08=83=F8=01=0F=84y=01=00=00=83=F8=01=0F=82S=01=00=00= +=83=F8=02u=3D=A1=AC=A6=05=08=85=C0=0F=847=01=00=00=B80=CA=04=08=89=F6=89E= +=F0=C7=04$=00=A8=05=08=E8=C9=CA=FF=FF=85=C0t0=A1=A8=A6=05=08=83=F8=02=0F=84= +=F2=00=00=00=83=F8=02wZHt=05=E8=8A=C7=FF=FF=A1=AC=A6=05=08=85=C0t?=B80=CC= +=04=08=8Dt&=00=89E=F0=8BE=F0=89D$=0C=B8x=00=00=00=89D$=08=A1=90=A6=05=08=89= +D$=04=A1=88=A6=05=08=89=04$=E8=EF=C7=FF=FF=8B]=F4=8Bu=F8=8B}=FC=89=EC]=C3= +=B8=F0=CB=04=08=EB=C3=8Dv=00=83=F8=03t=1C=83=F8=04u=9F=A1=AC=A6=05=08=85=C0= +t=07=B8p=CB=04=08=EB=A6=B8=E0=CA=04=08=EB=9F=A1=A4=A6=05=08=83=F8=01tJ=83= +=F8=01r'=83=F8=02=0F=85p=FF=FF=FF=8B=15=AC=A6=05=08=85=D2t=0A=B8P=CA=04=08= +=E9s=FF=FF=FF=B8=C0=C9=04=08=E9i=FF=FF=FF=8B=0D=AC=A6=05=08=85=C9t=0A=B80= +=C9=04=08=E9U=FF=FF=FF=B8=A0=C8=04=08=E9K=FF=FF=FF=8B=1D=AC=A6=05=08=85=DB= +t=0A=B8=10=C8=04=08=E97=FF=FF=FF=B8=80=C7=04=08=E9-=FF=FF=FF=A1=AC=A6=05=08= +=85=C0t=0A=B8=90=CD=04=08=E9=1A=FF=FF=FF=B8=E0=CC=04=08=E9=10=FF=FF=FF=B8= +P=C9=04=08=E9=C6=FE=FF=FF=A1=AC=A6=05=08=85=C0t=0A=B8=10=C9=04=08=E9=B3=FE= +=FF=FF=B80=C8=04=08=E9=A9=FE=FF=FF=A1=AC=A6=05=08=85=C0t=0A=B8=F0=C7=04=08= +=E9=96=FE=FF=FF=B8=10=C7=04=08=E9=8C=FE=FF=FF=A1=AC=A6=05=08=85=C0t=0A=B8= +=10=CC=04=08=E9y=FE=FF=FF=B8=D0=CB=04=08=E9o=FE=FF=FF=A1=AC=A6=05=08=85=C0= +t=0A=B8p=CD=04=08=E9\=FE=FF=FF=B8P=CC=04=08=E9R=FE=FF=FF=89=F6=8B=3D=AC=A6= +=05=08=85=FFt=0A=B8P=CB=04=08=E9<=FE=FF=FF=B8p=CA=04=08=E92=FE=FF=FF=89=F6= +=8B5=AC=A6=05=08=85=F6t=0A=B8=B0=CB=04=08=E9=1C=FE=FF=FF=B8=90=CB=04=08=E9= +=12=FE=FF=FF=89=F6U=89=E5=83=EC=18=89]=F8=89u=FC=A1=A0=A6=05=08=83=F8=04w= +J=FF$=85=F0i=05=081=DB;=1D=90=A6=05=08}91=F6=8D=B4&=00=00=00=00=A1=88=A6=05= +=08=01=F0=89=04$=E8!=0D=00=00=A1`=A6=05=08=8BP=14;P=18s$=C6=02=0A=FF@=14C= +=83=C6x;=1D=90=A6=05=08|=D2=89=F6=8B]=F8=8Bu=FC=89=EC]=C3=8D=B6=00=00=00=00= +=89=04$=B9=0A=00=00=00=89L$=04=E8=D7=C3=FF=FF=EB=CF1=DB;=1D=90=A6=05=08}=D3= +1=F6=90=A1=88=A6=05=08=01=F0=89=04$=E8A=02=00=00=A1`=A6=05=08=8BP=14;P=18= +s=1A=C6=02=0A=FF@=14=FF=05(=A7=05=08C=83=C6x;=1D=90=A6=05=08|=CC=EB=9A=89= +=04$=BA=0A=00=00=00=89T$=04=E8=81=C3=FF=FF=EB=D9=E8=A2=18=00=00=8B]=F8=8B= +u=FC=89=EC]=E94=11=00=00=E8=8F=18=00=00=8B]=F8=8Bu=FC=89=EC]=E9=81=13=00=00= +=8B]=F8=8Bu=FC=89=EC]=E9=03=16=00=00=8Dv=00U=89=E5=81=EC=B8=00=00=00=89]=F4= +=A1$=A3=05=08=89u=F8=89}=FC=85=C0x=0D=8B]=F4=8Bu=F8=8B}=FC=89=EC]=C31=C0=8D= +=9Dx=FF=FF=FF=BEd=00=00=00=89=85t=FF=FF=FF=8D=85t=FF=FF=FF=89=04$=E8?=C3=FF= +=FF=89=85p=FF=FF=FF=8B=3D=18=A3=05=08=8Dt&=00=8D=BC'=00=00=00=00=C6=03=01= +1=C0=89D$=141=C0=89D$=10=8B=85p=FF=FF=FF=89|$=08=89t$=04=89D$=0C=89=1C$=E8= +G#=00=00=85=C0u=18=80;=00t=13=8D=046=89=C6=8D@=0F=83=E0=F0)=C4=8D\$=18=EB= +=BB=89D$=041=F6=89t$=08=89=1C$=E8)_=00=00=A3$=A3=05=08=85=C0=0F=89W=FF=FF= +=FF1=DB1=C0=89=1D$=A3=05=08=E9H=FF=FF=FF=8D=B6=00=00=00=00=8D=BC'=00=00=00= +=00U=89=E5=8DE=F8=83=EC=18=89D$=04=C7=04$=00=00=00=00=E8=FF=C6=FF=FF=85=C0= +u=14=8BE=F8=A3=80=A2=05=08=8BE=FC=A3=84=A2=05=08=89=EC]=C31=C0=89D$=04=8D= +E=F0=89=04$=E8V=C5=FF=FF=85=C0u=18=8BE=F0=A3=80=A2=05=08=8BE=F4i=C0=E8=03= +=00=00=05=E7=03=00=00=EB=CA=C7=04$=00=00=00=00=E8=AE=C3=FF=FF=A3=80=A2=05= +=08=B8=FF=C9=9A;=EB=B2=8Dv=00=8D=BC'=00=00=00=00U=89=E5=83=EC=18=89]=F8=8B= +]=08=89u=FC=8B=0D=B4=A6=05=08=8Bu=0C=85=C9t;1=C0=85=C0t&=89D$=08=BA=1E^=05= +=08=89T$=04=89=1C$=E8q=C6=FF=FF=89]=08=8Bu=FC=8B]=F8=89=EC]=E9`=C3=FF=FF=89= +t$=08=B8$^=05=08=89D$=04=EB=D8=894$=E8QZ=00=00=EB=BD=EB=0D=90=90=90=90=90= +=90=90=90=90=90=90=90=90U=BA=88=14=00=00=89=E5W=8D=85H=EB=FF=FFVS=81=EC=BC= +=1C=00=00=8D]=D8=89=95l=E3=FF=FF=8BU=08=89\$=04=89=85p=E3=FF=FF=8BB=14=89= +=04$=E8=F8K=00=00=8BM=08=80yt=00=0F=84=D4=05=00=00=B0+=88E=E2=A1=A4=A6=05= +=08=C6E=E3=00=83=F8=01=0F=84=A3=05=00=00=83=F8=01=0F=82=8F=05=00=00=83=F8= +=02=0F=84l=05=00=00=89=F6=A1=DC=A6=05=08=8B=B5p=E3=FF=FF=85=C0=0F=85=F9=04= +=00=00=A1=B8=A6=05=08=85=C0=0F=85u=04=00=00=8BU=08=B9+^=05=08=8BB=18=89L$= +=04=894$=89D$=0C=89\$=08=E8=89=C5=FF=FF=894$=E8=81=C2=FF=FF=8B=15=88=A2=05= +=08=01=C6=85=D2=0F=85%=04=00=00=A1=8C=A2=05=08=85=C0t6=A1=B4=A6=05=08=85=C0= +=0F=84=FC=03=00=001=C0=85=C0=0F=84=DE=03=00=00=89D$=08=B8=1E^=05=08=89D$=04= +=894$=E8<=C5=FF=FF=894$=E84=C2=FF=FF=01=C6=80=3D=B0=A6=05=08=00=0F=85=99=03= +=00=00=8BM=08=8BA=14%=00=F0=00=00=3D=00 =00=00=0F=84O=03=00=00=3D=00`=00=00= +=0F=84D=03=00=00=8BE=081=FF=8B=15=94=A2=05=08=8BX4=8BH0=A1=90=A2=05=08=89= +\$=04=BB4^=05=08=89D$=18=B8=01=00=00=00=89D$=10=A1=BC=A6=05=08=89T$=1C=89= +|$=14=89D$=0C=8D=85=98=E3=FF=FF=89D$=08=89=0C$=E8=8DM=00=00=89D$=08=89\$=04= +=894$=E8=A5=C4=FF=FF=894$=E8=9D=C1=FF=FF=01=C6=8D=85t=E3=FF=FF=89=04$=E8=0D= +=C0=FF=FF=89=85d=E3=FF=FF=85=C0=0F=84o=02=00=00=8B=0D=80=A2=05=08=8B=95t=E3= +=FF=FF9=D1=0F=8CE=02=00=009=D1=0F=84+=02=00=00=8D=81T=3D=0F=FF1=DB9=D0=7F= +=109=CA=0F=8C=0D=02=00=009=CA=0F=84=F3=01=00=00=8B<=9D=18=A3=05=08=90=8D=B4= +&=00=00=00=00=C6=06=01=8B=85d=E3=FF=FF=89|$=08=8B=95l=E3=FF=FF=894$=8B=8D= +h=E3=FF=FF=89D$=0C=8B=85p=E3=FF=FF=89L$=141=C9=01=D0=89L$=10)=F0H=89D$=04= +=E8=C2=1F=00=00=85=C0uK=80>=00tF=8B=85l=E3=FF=FF=8B=95p=E3=FF=FF=01=C0=89= +=85l=E3=FF=FF=83=C0=0F=83=E0=F0)=C4=8B=85p=E3=FF=FF=8D\$ =89T$=04=89=1C$)= +=C6=89t$=08=8D4=1E=E8=B6=C1=FF=FF=89=9Dp=E3=FF=FF=E9s=FF=FF=FF=01=C6=C6=06= + F=C6=06=00=A1=C8=A6=05=08=85=C0=0F=85=0E=01=00=00=A1`=A6=05=08=BB@=A7=05= +=08=89D$=04=8B=85p=E3=FF=FF=89=04$=E8=09=BF=FF=FF=89\$=0C=8BU=08=8B=BDp=E3= +=FF=FF=8B=0D=94=A6=05=08=8BBl)=FE=015(=A7=05=08=85=C9=89D$=08=0F=84=BD=00= +=00=00=85=C0=0F=84=B5=00=00=00=8BBh=8D=B6=00=00=00=00=8D=BF=00=00=00=00=89= +D$=04=8BU=08=8B=02=89=04$=E8=FF=05=00=00=8BM=08=83yp=0At"=8B=1D=CC=A6=05=08= +=85=DBu=08=8De=F4[^_]=C3=8BU=08=8BB=14=89=04$=E8v=08=00=00=EB=E8=8BAd=85=C0= +t=E1=C7=04$9^=05=08=A1`=A6=05=081=FF=89D$=0C=B8=04=00=00=00=89D$=08=B8=01= +=00=00=00=89D$=04=E8=BC=BE=FF=FF=89|$=0C=8BU=08=83=05(=A7=05=08=04=8BBlH=89= +D$=08=8BBh=89D$=04=8BBd=89=04$=E8|=05=00=00=8B5=CC=A6=05=08=85=F6t=86=8BM= +=08=8BAh=EB=8C=8BM=08=8BA=14=E9O=FF=FF=FF=C7=04$=FE]=05=08=A1`=A6=05=08=89= +D$=0C=B8=02=00=00=00=89D$=08=B8=01=00=00=00=89D$=04=E8P=BE=FF=FF=83=05(=A7= +=05=08=02=E9=BF=FE=FF=FF=8B=95h=E3=FF=FF;=15=84=A2=05=08=0F=8F=FB=FD=FF=FF= +=BB=01=00=00=00=E9=F1=FD=FF=FF=8B=85h=E3=FF=FF9=05=84=A2=05=08=0F=8D=C3=FD= +=FF=FF=E8=F9=FA=FF=FF=8B=0D=80=A2=05=08=8B=95t=E3=FF=FF=E9=AD=FD=FF=FF=8D= +=85x=E3=FF=FF=89D$=08=8B=85t=E3=FF=FF=89=C1=89=04$=C1=F9=1F=89L$=04=E8=D7= +X=00=00=89=C3=E8=E0=F9=FF=FF=89D$=08=B8>^=05=08=894$=89\$=0C=89D$=04=E8=CF= +=C1=FF=FF=894$=E8=C7=BE=FF=FF=01=C6=E9=1E=FE=FF=FF=8BU=08=0F=B6B$=89D$=0C= +=8BB$=8BR(=894$=0F=AC=D0=08%=FF=00=00=00=89D$=08=B8C^=05=08=89D$=04=E8=91= +=C1=FF=FF=E9=E7=FC=FF=FF=8BU=08=8BB=1C=894$=89D$=04=E8=D2=FA=FF=FF=01=C6=E9= +N=FC=FF=FF=8BM=08=8BA =89D$=08=B8$^=05=08=E9=17=FC=FF=FF=8BU=08=8BB =89=04= +$=E8=C9V=00=00=E9=F3=FB=FF=FF=8BM=08=8BA=1C=894$=89D$=04=E8=92=FA=FF=FF=01= +=C6=E9=C2=FB=FF=FF=A1=C0=A6=05=08=BF>^=05=08=8B=15=C4=A6=05=08=8BM=08=89D= +$=18=B8=00=02=00=00=89D$=101=C0=89D$=14=A1=BC=A6=05=08=89T$=1C=89D$=0C=8D= +=85(=E6=FF=FF=89D$=08=8BA<=8BQ@=89=04$=89T$=04=E8=BCI=00=00=89D$=0C=A1=9C= +=A6=05=08=894$=89|$=04=89D$=08=E8=CB=C0=FF=FF=894$=E8=C3=BD=FF=FF=01=C6=E9= +=14=FB=FF=FF=8BM=08=8D=85=B8=E8=FF=FF=89D$=08=8BQ`=8BA\=89T$=04=89=04$=E8= +ug=00=00=89D$=0C=B8=07=00=00=00=89D$=08=B8>^=05=08=89D$=04=8B=85p=E3=FF=FF= +=89=04$=E8y=C0=FF=FF=8B=95p=E3=FF=FF=89=14$=E8k=BD=FF=FF=8B=B5p=E3=FF=FF=01= +=C6=E9=A9=FA=FF=FF=8BM=08=8BQD=8BAH=89=95t=E3=FF=FF=89=85h=E3=FF=FF=E9|=FA= +=FF=FF=8BM=08=8BQL=8BAP=EB=E4=8BE=08=8BPT=89=95t=E3=FF=FF=8BPX=89=95h=E3=FF= +=FF=E9W=FA=FF=FF=B0 =E9'=FA=FF=FFU=B8=FF=FF=FF=FF=89=E5WVS=81=ECL =00=00=BB= +=00 =00=00=89\$=04=8B}=0C=8D=9D=E8=DF=FF=FF=89D$=0C=8Bu=10=89=1C$=89|$=08= +=89t$=10=E8Fb=00=00=89=85=D8=DF=FF=FF=3D=FF=1F=00=00=89=9D=D4=DF=FF=FF=0F= +=87f=02=00=00=8B=3D=F8=A6=05=08=85=FF=0F=85=D1=00=00=00=8BE=14=85=C0t*=E8= +}=BE=FF=FF=83=F8=01=0F=87=93=00=00=00=8B=B5=D4=DF=FF=FF=8B=85=D8=DF=FF=FF= +=89=F7=01=C71=C09=FE=89=85=D0=DF=FF=FFrO=8Bu=08=85=F6t(=8BE=08=BB=01=00=00= +=00=8B=95=D8=DF=FF=FF=89\$=04=89D$=0C=8B=85=D4=DF=FF=FF=89T$=08=89=04$=E8= +I=BB=FF=FF=8BM=14=85=C9t=0B=8B=85=D0=DF=FF=FF=8BU=14=89=02=8B=85=D8=DF=FF= +=FF=8De=F4[^_]=C3=E8T=BF=FF=FF=8B=10=8Dv=00=8D=BC'=00=00=00=00=0F=B6=06=F6= +DB=01@t=06=FF=85=D0=DF=FF=FFF9=FEr=EB=EB=891=C0=8B=95=D4=DF=FF=FF=89D$=08= +=8B=85=D8=DF=FF=FF=89=14$=89D$=04=E8=CBV=00=00=89=85=D0=DF=FF=FF=E9`=FF=FF= +=FF=E8=B3=BD=FF=FF=83=F8=01=0F=867=01=00=00=8B=BD=D4=DF=FF=FF1=DB=8B=B5=D8= +=DF=FF=FF=89=9D=D0=DF=FF=FF=89=FA=01=F2=89=95=CC=DF=FF=FF9=D7=89=FEs8=0F=B6= +=17=0F=BE=C2=83=F8_=0F=8F=F9=00=00=00=83=F8A}=12=83=F8 |2=83=F8#~=08=83=E8= +%=83=F8=1Aw%=88=16G=FF=85=D0=DF=FF=FFF;=BD=CC=DF=FF=FFr=C8=8B=85=D4=DF=FF= +=FF)=C6=89=B5=D8=DF=FF=FF=E9=E3=FE=FF=FF1=C91=D2=89=8D=E0=DF=FF=FF=89=95=E4= +=DF=FF=FF=8Dv=00=89|$=04=8D=85=E0=DF=FF=FF=8D=95=DC=DF=FF=FF=89D$=0C=8B=85= +=CC=DF=FF=FF=89=14$)=F8=89D$=08=E8=90=BB=FF=FF=83=F8=FF=89=C3tx=83=F8=FEt= +e=85=C0u=05=BB=01=00=00=00=8B=85=DC=DF=FF=FF=89=04$=E8=ED=BD=FF=FF=85=C0=89= +=C2x:=85=DBt=15=8Dt&=00=8D=BC'=00=00=00=00=0F=B6=07G=88=06FKu=F6=01=95=D0= +=DF=FF=FF=8D=85=E0=DF=FF=FF=89=04$=E8z=BD=FF=FF=85=C0=0F=84z=FF=FF=FF=E9G= +=FF=FF=FF=C6=06?=01=DFF=FF=85=D0=DF=FF=FF=EB=D7=8B=BD=CC=DF=FF=FF=C6=06?=E9= +$=FF=FF=FFG=EB=F5=83=E8a=83=F8=1D=E9=11=FF=FF=FF=8B=B5=D4=DF=FF=FF=8B=85=D8= +=DF=FF=FF=89=F7=01=C79=FEr=11=8B=95=D8=DF=FF=FF=89=95=D0=DF=FF=FF=E9=F6=FD= +=FF=FF=E8=99=BD=FF=FF=89=C1=0F=B6=16=8B=01=F6DP=01@u=03=C6=06?F9=FEr=EC=EB= +=D2=89=C2=B9=FF=FF=FF=FF=83=C0=10=83=E0=F0B)=C4=89t$=10=8DD$=14=89=85=D4=DF= +=FF=FF=89L$=0C=89|$=08=89T$=04=89=04$=E8=97_=00=00=E9c=FD=FF=FF=89=F6U=89= +=E5VS=83=EC=10=8B=0D=D0=A6=05=08=8Bu=08=8BU=0C=85=C9=8BE=10=8B]=14=0F=85=DE= +=00=00=00=85=DBt6=A1=C8=A6=05=08=85=C0t-=8BS=0C=8DB=04;C=10=0F=87=A8=00=00= +=00=89=14$=B8=04=00=00=00=B9(=A7=05=08=89D$=08=89L$=04=E8=D0=BA=FF=FF=83C= +=0C=04=89t$=04=A1=FC=A6=05=081=D2=89T$=0C=89D$=08=A1`=A6=05=08=89=04$=E8=94= +=FC=FF=FF=01=05(=A7=05=08=85=DBt2=A1=C8=A6=05=08=85=C0t)=8BS=0C=8DB=04;C=10= +w6=89=14$=B8=04=00=00=00=89D$=08=B8(=A7=05=08=89D$=04=E8t=BA=FF=FF=83C=0C= +=04=A1=D0=A6=05=08=85=C0u=07=83=C4=10[^]=C3=83=C4=10[^]=EBP=89=1C$=B8=04=00= +=00=00=89D$=04=E8=97=BB=FF=FF=8BS=0C=EB=B4=89=1C$=B8=04=00=00=00=89D$=04=E8= +=81=BB=FF=FF=8BS=0C=E9?=FF=FF=FF=90=89D$=08=89T$=04=894$=E8@=02=00=00=E9=0D= +=FF=FF=FF=8Dt&=00=8D=BC'=00=00=00=00U=89=E5=83=EC=08=A1=B4=A2=05=08=85=C0= +t=11=C7=04$=B0=A2=05=08=E8=95=03=00=00=89=EC]=C3=90=C7=04$=A0=A2=05=08=E8= +=84=03=00=00=C7=04$=B8=A2=05=08=E8x=03=00=00=C7=04$=A8=A2=05=08=EB=D5=EB=0D= +=90=90=90=90=90=90=90=90=90=90=90=90=90U=89=E5=81=EC=B8=02=00=00=89]=FC=A1= +=DC=A6=05=08=8B]=08=85=C0=0F=85=D4=00=00=00=A1=B8=A6=05=08=85=C0u`1=C9=8B= +=15=94=A6=05=08=89L$=0C=8BCl=85=D2=89D$=08tD=85=C0t@=8BCh=8D=B6=00=00=00=00= +=8D=BC'=00=00=00=00=89D$=04=8B=03=89=04$=E82=FE=FF=FF=A1=CC=A6=05=08=85=C0= +u=09=8B]=FC=89=EC]=C3=89=F6=8BC=14=89=04$=E8=B5=00=00=00=EB=EA=8Dv=00=8BC= +=14=EB=CB=A1=C0=A6=05=08=8B=15=C4=A6=05=08=89D$=18=B8=00=02=00=00=89D$=10= +1=C0=89D$=14=A1=BC=A6=05=08=89T$=1C=89D$=0C=8D=85h=FD=FF=FF=89D$=08=8BC<=8B= +S@=89=04$=89T$=04=E8=C4C=00=00=89D$=08=A1=9C=A6=05=08=C7=04$>^=05=08=89D$= +=04=E8=C3=B8=FF=FF=E9;=FF=FF=FF=8D=B6=00=00=00=00=8D=85h=FD=FF=FF=89D$=08= +=8BC\=8BS`=89=04$=89T$=04=E8=84a=00=00=89D$=08=B8=07=00=00=00=89D$=04=C7=04= +$>^=05=08=E8=83=B8=FF=FF=E9=F2=FE=FF=FF=8D=B6=00=00=00=00U=89=E5=83=EC=08= +=8BM=08=89=CA=81=E2=00=F0=00=00=81=FA=00=80=00=00tx=81=FA=00@=00=00=B8/=00= +=00=00t:=81=FA=00=A0=00=00=B8@=00=00=00t-=81=FA=00=10=00=00=B8|=00=00=00t= + =81=FA=00=C0=00=00t=0C=90=8D=B4&=00=00=00=00=89=EC]=C3=B8=3D=00=00=00=8D= +=B4&=00=00=00=00=8B=15`=A6=05=08=8BJ=14;J=18s=12=88=01=FFB=14=FF=05(=A7=05= +=08=EB=D5=90=8Dt&=00=89=14$=0F=B6=C0=89D$=04=E89=B5=FF=FF=EB=E2=83=3D=CC=A6= +=05=08=01u=B6=F6=C1I=B8*=00=00=00u=BC=EB=AA=8Dv=00=8D=BC'=00=00=00=00U=89= +=E5W=BF=04=00=00=00VS=83=EC=1C=8BU=10=8BE=08=8BM=0C=83=FA=FF=89E=F0=0F=84= +>=01=00=00=89=C8%=00=F0=00=00=3D=00@=00=00=0F=84"=01=00=00=3D=00=A0=00=00= +=0F=84=F6=00=00=00=3D=00=10=00=00=0F=84=E1=00=00=00=3D=00=C0=00=00=0F=84=CC= +=00=00=00=3D=00`=00=00=0F=84=B7=00=00=00=3D=00 =00=00=0F=84=A2=00=00=00=F6= +=C1It=0F=BF=0D=00=00=00=8Dv=00=8D=BC'=00=00=00=001=DB=83=FF=04t9=C7=04$=A0= +=A2=05=08=E8=ED=00=00=00=85=DB=8D=04=FD=A0=A2=05=08t=03=8DC=08=89=04$=E8=D7= +=00=00=00=C7E=08=A8=A2=05=08=83=C4=1C[^_]=E9=C4=00=00=00=8Dt&=00=8BE=F0=89= +=04$=E8=0D=B6=FF=FF=01E=F0=89=C6=8B=1D=80=A6=05=08=85=DBt=AD=8D=B6=00=00=00= +=00=8B=139=F2w=1C=8BE=F0=89T$=08)=D0=8BS=04=89=04$=89T$=04=E8j=B6=FF=FF=85= +=C0t=85=8B[=10=85=DBu=D7=E9y=FF=FF=FF=BF=0A=00=00=00=E9h=FF=FF=FF=BF=09=00= +=00=00=E9^=FF=FF=FF=BF=08=00=00=00=E9T=FF=FF=FF=BF=07=00=00=00=E9J=FF=FF=FF= +=85=D2u=13=A1=04=A3=05=08=85=C0t=0A=BF=0C=00=00=00=E93=FF=FF=FF=BF=06=00=00= +=00=E9)=FF=FF=FF=BF=05=00=00=00=E9=1F=FF=FF=FF=8B=1D=FC=A2=05=08=85=DB=0F= +=84=B4=FE=FF=FF1=DB=BF=0B=00=00=00=E9=0C=FF=FF=FF=90=8Dt&=00U=89=E5VS=83=EC= +=10=8BE=08=8B=18=8Bp=04=85=DB~(=8D=B6=00=00=00=00=8D=BF=00=00=00=00=0F=BE= +=06F=8B=15`=A6=05=08=8BJ=14;J=18s=11=88=01=FFB=14K=85=DB=7F=E4=83=C4=10[^= +]=C3=89=14$=0F=B6=C0=89D$=04=E8F=B3=FF=FF=EB=E3=8D=B6=00=00=00=00=8D=BF=00= +=00=00=00U1=C0=89=E5=83=EC=18=8BU=08=89]=FC=8B=1A=85=DB~=1F=89\$=08=8BB=04= +=C7=04$=01=00=00=00=89D$=04=E8=95>=00=009=D8=0F=95=C0=0F=B6=C0=8B]=FC=89=EC= +]=C3=8D=B6=00=00=00=00U=89=E5=83=EC(=89]=F8=A1=DC=A6=05=081=DB=89u=FC=85=C0= +=8Bu=08t=05=BB=08=00=00=00=8B=0D=B8=A6=05=08=85=C9t=0D=8B=15=9C=A6=05=08=89= +=D8=01=D0=8DX=01=8DE=F4=89D$=0C=A1=FC=A6=05=08=89D$=08=8B=06=C7=04$=00=00= +=00=00=89D$=04=E8H=F7=FF=FF=8BE=F4=8B=0D=CC=A6=05=08=01=C3=85=C9t9=8BF=14= +=89=C2=81=E2=00=F0=00=00=81=FA=00=80=00=00t9=81=FA=00@=00=00t.=81=FA=00=A0= +=00=00t&=81=FA=00=10=00=00t=1E=81=FA=00=C0=00=00t=16=8D=B6=00=00=00=00=89= +=D8=8Bu=FC=8B]=F8=89=EC]=C3=8Dt&=00C=EB=EDIu=EA=A8It=E6=EB=F4=8Dt&=00U=89= +=E5WVS=83=EC<=A1$=A7=05=08=8B=15=90=A6=05=08=89E=DC9=D0=89U=D4~=03=89U=DC= +=C7E=EC=00=00=00=00=8BM=D49M=EC=0F=8D=DA=00=00=00=C7E=C8=00=00=00=00=8BE=C8= +=8B=3D=88=A6=05=08=01=F8=89=04$=E8=F2=FE=FF=FF=89E=E41=C9;M=DC=0F=8D=F1=01= +=00=00=A1=90=A6=05=08=8B=15 =A7=05=08=89E=D4=89U=D0=8Dv=00=8D=04I=8D=1C=85= +=00=00=00=00=8BE=D0=8B4=18=85=F6=0F=84=BD=01=00=00=8BU=D4=8Dq=01=01=CA=89= +=D0=99=F7=FE=89=C7=8BE=EC=99=F7=FF=8BU=E49=C8=89=C7t=03=83=C2=02=8BM=D0=8B= +L=19=08=8B=04=B9=89M=C09=C2~6=89=D1)=C1=89=C8=8BM=D0=01D=19=04=8BE=C0=89=14= +=B8=A1=10=A7=05=08=8B=15=90=A6=05=089D=19=04=89U=D4=0F=9C=C0=0F=B6=C0=89=04= +=19=90=8D=B4&=00=00=00=00;u=DC=89=F1=0F=8Cu=FF=FF=FF=FFE=EC=8BE=D4=83E=C8= +x9E=EC=0F=8C-=FF=FF=FF=8BU=DC=83=FA=01=0F=8E=1E=01=00=00=8B=0D =A7=05=08=8D= +=04R=8DD=81=F4=89M=D0=8B=08=85=C9u=09J=83=E8=0C=83=FA=01=7F=F1=8BM=D0=8D=04= +R=8DD=81=F4=89E=F0=89=D1=8BE=D4=99=F7=F9=85=D2=89E=E0t=04@=89E=E0=C7E=E8=00= +=00=00=00=8BE=E09E=E8=0F=8D=B1=00=00=00=C7E=C4=00=00=00=00=8BU=E0=C1=E0=04= +)=D0=89E=CC=C7E=D8=00=00=00=00=8BM=E81=FF=8Bu=C4=89M=EC=A1=88=A6=05=08=8D= +=1C=F5=00=00=00=00=01=D8=89=04$=E8=9A=F9=FF=FF=A1=88=A6=05=08=01=C3=89=1C= +$=E8=8B=FD=FF=FF=89E=E4=8BU=F0=8BM=D8=8BB=08=8BU=E0=01U=EC=8B=04=88A=89M=D8= +=8BM=CC=01=CE=8BM=EC;=0D=90=A6=05=08}=19=8BU=E4=8D=1C8=89\$=04=8D=04:=89=DF= +=89=04$=E8;=04=00=00=EB=99=A1`=A6=05=08=8BP=14;P=18s!=C6=02=0A=FF@=14=FFE= +=E8=8BM=E0=83E=C4=0F9M=E8=0F=8Ca=FF=FF=FF=83=C4<[^_]=C3=89=04$=BA=0A=00=00= +=00=89T$=04=E8=02=B0=FF=FF=EB=D2=A1 =A7=05=08=89E=D0=E9=F4=FE=FF=FF=8Dq=01= +=E9=A3=FE=FF=FF=8B=0D=90=A6=05=08=89M=D4=E9=A0=FE=FF=FF=90=8Dt&=00U=89=E5= +WVS=83=EC,=A1$=A7=05=08=89E=E0=A1=90=A6=05=089E=E0~=03=89E=E0=C7E=EC=00=00= +=00=009E=EC=0F=8D=E2=00=00=00=C7E=D4=00=00=00=00=8Dt&=00=8D=BC'=00=00=00=00= +=8B=15=88=A6=05=08=8BE=D4=01=D0=89=04$=E8=8D=FC=FF=FF=C7E=D0=00=00=00=00=8B= +U=E09U=D0=89E=E8=0F=8D=91=00=00=00=A1 =A7=05=08=89E=D8=EB=0D=90=90=90=90=90= +=90=90=90=90=90=90=90=90=8BU=D0=8D=04R=8D=1C=85=00=00=00=00=8BE=D8=8B=04=18= +=85=C0=0F=84=E5=01=00=00=8BE=EC=89=D6F=8BM=E8=99=F7=FE;U=D0=89=D7t=03=83=C1= +=02=8BE=D8=8BD=18=08=89E=D0=8B=04=B89=C1~.=89=CA)=C2=89=D0=8BU=D8=01D=1A=04= +=8BE=D0=89=0C=B8=A1=10=A7=05=089D=1A=04=0F=9C=C0=0F=B6=C0=89=04=1A=89=F6=8D= +=BC'=00=00=00=00=89u=D0=8BE=E09=C6|=86=FFE=EC=83E=D4x=8BU=EC;=15=90=A6=05= +=08=0F=8C0=FF=FF=FF=8Bu=E0=83=FE=01=0F=8ER=01=00=00=A1 =A7=05=08=89E=D8=8B= +U=D8=8D=04v=8DD=82=F4=89=F6=8B8=85=FFu=09N=83=E8=0C=83=FE=01=7F=F1=C7E=E4= +=00=00=00=00=8BU=D8=8D=04v=8DD=82=F4=89E=F0=A1=88=A6=05=08=89=04$=E8=80=F7= +=FF=FF=A1=88=A6=05=08=89=04$=E8s=FB=FF=FF=89E=E8=8BU=F0=C7E=EC=01=00=00=00= +=8BB=08=8B=08=B8=01=00=00=00;=05=90=A6=05=08}}=BFx=00=00=00=EB=0D=90=90=90= +=90=90=90=90=90=90=90=90=90=90=8BE=EC=99=F7=FE=85=D2=89U=DC=0F=85=9C=00=00= +=00=A1`=A6=05=08=8BP=14;P=18sy=C6=02=0A=FF@=14=C7E=E4=00=00=00=00=A1=88=A6= +=05=08=01=F8=89=04$=E8=06=F7=FF=FF=A1=88=A6=05=08=01=F8=83=C7x=89=04$=E8=F4= +=FA=FF=FF=89E=E8=8BU=F0=FFE=EC=8BB=08=8BU=DC=8B=0C=90=8BE=EC;=05=90=A6=05= +=08|=97=A1`=A6=05=08=8BP=14;P=18s=0E=C6=02=0A=FF@=14=83=C4,[^_]=C3=89=04$= +=BA=0A=00=00=00=89T$=04=E8=A3=AD=FF=FF=EB=E5=89=04$=BB=0A=00=00=00=89\$=04= +=E8=90=AD=FF=FF=E9w=FF=FF=FF=8B]=E4=8BE=E4=01=CB=89\$=04=8BM=E8=01=C8=89=04= +$=E8j=01=00=00=89]=E4=E9]=FF=FF=FF=A1 =A7=05=08=89E=D8=E9=C4=FE=FF=FF=90=8D= +t&=00=8BU=D0=8Dr=01=E9e=FE=FF=FF=90=8Dt&=00U=89=E5W1=FFVS=83=EC=0C1=DB;=1D= +=90=A6=05=08|.=A1`=A6=05=08=8BP=14;P=18s=0E=C6=02=0A=FF@=14=83=C4=0C[^_]=C3= +=89=04$=B9=0A=00=00=00=89L$=04=E8=07=AD=FF=FF=EB=E5=89}=F0=8B=15=88=A6=05= +=08=89=D8=C1=E0=04)=D8=8Ds=01=8D=04=C2=89=04$=E8=EF=F9=FF=FF=01=C7;5=90=A6= +=05=08}=03=83=C7=02=8BM=F0=85=C9t+;=3D=10=A7=05=08|#=A1`=A6=05=08=8BP=14;= +P=18=0F=83=98=00=00=00=C6=02=0A=FF@=14=8BM=F0)=CF=8D=B4&=00=00=00=00=8B=15= +=88=A6=05=08=89=D8=C1=E0=04)=D8=8D=04=C2=89=04$=E8=98=F5=FF=FF=A1=90=A6=05= +=089=C6}+=A1`=A6=05=08=8BP=14;P=18sE=C6=02,=FF@=14=A1`=A6=05=08=8BP=14;P=18= +s=1F=C6=02 =FF@=14=A1=90=A6=05=089=C6=89=F3=0F=8CM=FF=FF=FF=E9=1A=FF=FF=FF= +=90=8Dt&=00=89=04$=BB =00=00=00=89\$=04=E87=AC=FF=FF=EB=D4=89=04$=BA,=00=00= +=00=89T$=04=E8$=AC=FF=FF=EB=AE=89=04$=BA=0A=00=00=00=89T$=04=E8=11=AC=FF=FF= +=E9X=FF=FF=FF=8Dt&=00U=89=E5WVS=83=EC=1C=8B]=08=8B}=0C9=FB}Y=8D=B6=00=00=00= +=00=8D=BC'=00=00=00=00=8B=0D=04=A7=05=08=85=C9=0F=8E=85=00=00=00=89=F8=8D= +s=01=99=F7=F9=89E=F0=89=F0=99=F7=F99E=F0=89E=EC~A=A1`=A6=05=08=8BP=14;P=18= +s!=C6=02=09=FF@=14=8B5=04=A7=05=08=89=D8=99=F7=FE)=D6=01=F39=FB|=B4=83=C4= +=1C[^_]=C3=89=04$=BE=09=00=00=00=89t$=04=E8=83=AB=FF=FF=EB=D2=A1`=A6=05=08= +=8BP=14;P=18s=0C=C6=02 =FF@=14=89=F3=EB=CA=89=F6=89=04$=BB =00=00=00=89\$= +=04=E8W=AB=FF=FF=EB=E7=8Ds=01=EB=CF=90=8D=B4&=00=00=00=00U=89=E5=8BM=08V=8B= +u=0CS=8B]=10=89=F2=0F=B6=06<.tK=84=C0t=10=90=8Dt&=00=88=01BA=0F=B6=02=84=C0= +u=F59=F2v=11=80z=FF/t=0B=C6=01/A=8D=B4&=00=00=00=00=0F=B6=03=84=C0t=14=89= +=F6=8D=BC'=00=00=00=00=88=01CA=0F=B6=03=84=C0u=F5=C6=01=00[^]=C3=80~=01=00= +t=D8=EB=AD=8D=B6=00=00=00=00U=BAVUUU=89=E5WVS=83=EC=1C=8B=0D=10=A7=05=08=C7= +E=F0=00=00=00=00=89=C8=F7=EA=89=C8=C1=F8=1F)=C2=85=D2=89=15$=A7=05=08u=0A= +=B8=01=00=00=00=A3$=A7=05=08=A1 =A7=05=08=85=C0=0F=84=92=00=00=001=F6;5$=A7= +=05=08}o=C7E=EC=04=00=00=001=FF=C7E=E8=00=00=00=00=EB=0D=90=90=90=90=90=90= +=90=90=90=90=90=90=90=8BE=E8=8B=1D =A7=05=08=83=C0=03=89D;=04=8BE=F0=C7=04= +;=01=00=00=00=85=C0u:1=D29=F2=7F=18=8B=1D =A7=05=08=8BD;=08=89=F6=C7=04=90= +=03=00=00=00B9=F2~=F4=83E=E8=03F=83=C7=0C=83E=EC=04;5$=A7=05=08|=B0=83=C4= +=1C[^_]=C3=8BE=EC=89=04$=E8=9DW=00=00=89D;=08=EB=B5=A1$=A7=05=08=8D=04@=C1= +=E0=02=89=04$=E8=84W=00=00=A3 =A7=05=08=C7E=F0=01=00=00=00=E9J=FF=FF=FF=8D= +v=00U=89=E5S=83=EC=14=8B]=08=85=DBtB=C7=04$=00=00=00=00=B8=05=00=00=00=89= +D$=08=B8=A0m=05=08=89D$=04=E8<=AA=FF=FF=89D$=04=8B=15=EC=A9=05=08=A1d=A6=05= +=08=89T$=08=89=04$=E8=01=AA=FF=FF=89=1C$=E8I=AD=FF=FF=90=C7=04$=00=00=00=00= +=B8=05=00=00=00=89D$=08=B8=E0m=05=08=89D$=04=E8=FA=A9=FF=FF=89=04$=8B=15=EC= +=A9=05=08=89T$=04=E8=08=AC=FF=FF=C7=04$=00=00=00=00=B9 n=05=08=B8=05=00=00= +=00=89L$=04=89D$=08=E8=CA=A9=FF=FF=89=04$=8B=15`=A6=05=08=89T$=04=E8x=A9=FF= +=FF=C7=04$=00=00=00=00=BA=05=00=00=00=B8=C0n=05=08=89T$=08=89D$=04=E8=9A=A9= +=FF=FF=89=04$=8B=15`=A6=05=08=89T$=04=E8H=A9=FF=FF=C7=04$=00=00=00=00=B8=05= +=00=00=00=89D$=08=B8 o=05=08=89D$=04=E8j=A9=FF=FF=89=04$=8B=15`=A6=05=08=89= +T$=04=E8=18=A9=FF=FF=C7=04$=00=00=00=00=B8=05=00=00=00=89D$=08=B8@p=05=08= +=89D$=04=E8:=A9=FF=FF=89=04$=8B=15`=A6=05=08=89T$=04=E8=E8=A8=FF=FF=C7=04= +$=00=00=00=00=B9=E0q=05=08=B8=05=00=00=00=89L$=04=89D$=08=E8=0A=A9=FF=FF=89= +=04$=8B=15`=A6=05=08=89T$=04=E8=B8=A8=FF=FF=C7=04$=00=00=00=00=BA=05=00=00= +=00=B8=A0s=05=08=89T$=08=89D$=04=E8=DA=A8=FF=FF=89=04$=8B=15`=A6=05=08=89= +T$=04=E8=88=A8=FF=FF=C7=04$=00=00=00=00=B8=05=00=00=00=89D$=08=B8=00u=05=08= +=89D$=04=E8=AA=A8=FF=FF=89=04$=8B=15`=A6=05=08=89T$=04=E8X=A8=FF=FF=C7=04= +$=00=00=00=00=B8=05=00=00=00=89D$=08=B8@w=05=08=89D$=04=E8z=A8=FF=FF=89=04= +$=8B=15`=A6=05=08=89T$=04=E8(=A8=FF=FF=C7=04$=00=00=00=00=B9=A0x=05=08=B8= +=05=00=00=00=89L$=04=89D$=08=E8J=A8=FF=FF=89=04$=8B=15`=A6=05=08=89T$=04=E8= +=F8=A7=FF=FF=C7=04$=00=00=00=00=BA=05=00=00=00=B8 z=05=08=89T$=08=89D$=04= +=E8=1A=A8=FF=FF=89=04$=8B=15`=A6=05=08=89T$=04=E8=C8=A7=FF=FF=C7=04$=00=00= +=00=00=B8=05=00=00=00=89D$=08=B8=80{=05=08=89D$=04=E8=EA=A7=FF=FF=89=04$=8B= +=15`=A6=05=08=89T$=04=E8=98=A7=FF=FF=C7=04$=00=00=00=00=B8=05=00=00=00=89= +D$=08=B8@}=05=08=89D$=04=E8=BA=A7=FF=FF=89=04$=8B=15`=A6=05=08=89T$=04=E8= +h=A7=FF=FF=C7=04$=00=00=00=00=B9=00~=05=08=B8=05=00=00=00=89L$=04=89D$=08= +=E8=8A=A7=FF=FF=89=04$=8B=15`=A6=05=08=89T$=04=E88=A7=FF=FF=C7=04$=00=00=00= +=00=BA=05=00=00=00=B8=E0=7F=05=08=89T$=08=89D$=04=E8Z=A7=FF=FF=89=04$=8B=15= +`=A6=05=08=89T$=04=E8=08=A7=FF=FF=C7=04$=00=00=00=00=B8=05=00=00=00=89D$=08= +=B8`=82=05=08=89D$=04=E8*=A7=FF=FF=89=04$=8B=15`=A6=05=08=89T$=04=E8=D8=A6= +=FF=FF=C7=04$=00=00=00=00=B8=05=00=00=00=89D$=08=B8=C0=83=05=08=89D$=04=E8= +=FA=A6=FF=FF=89=04$=8B=15`=A6=05=08=89T$=04=E8=A8=A6=FF=FF=C7=04$=00=00=00= +=00=B9=E0=84=05=08=B8=05=00=00=00=89L$=04=89D$=08=E8=CA=A6=FF=FF=89=04$=8B= +=15`=A6=05=08=89T$=04=E8x=A6=FF=FF=C7=04$=00=00=00=00=BA=05=00=00=00=B8 =85= +=05=08=89T$=08=89D$=04=E8=9A=A6=FF=FF=89=04$=8B=15`=A6=05=08=89T$=04=E8H=A6= +=FF=FF=C7=04$=00=00=00=00=B8=05=00=00=00=89D$=08=B8`=85=05=08=89D$=04=E8j= +=A6=FF=FF=89=04$=8B=15`=A6=05=08=89T$=04=E8=18=A6=FF=FF=C7=04$=00=00=00=00= +=B8=05=00=00=00=89D$=08=B8=00=86=05=08=89D$=04=E8:=A6=FF=FF=89=04$=8B=15`= +=A6=05=08=89T$=04=E8=E8=A5=FF=FF=C7=04$=00=00=00=00=B8=05=00=00=00=B9O^=05= +=08=89D$=08=89L$=04=E8=0A=A6=FF=FF=89=04$=BAf^=05=08=89T$=04=E8=19=A8=FF=FF= +=E9=D3=FB=FF=FF=90=90=90=90=90=90=90=90=90=90=90=90U1=D2=89=E5=83=EC=08=8B= +E=0C=8B@=10%=00=F0=00=00=3D=00=A0=00=00t=11=8BE=08=89=04$=E8=B3=A6=FF=FF=85= +=C0=89=C2x=06=89=EC=89=D0]=C3=E8=12=A6=FF=FF=8B=00=83=F8&t=0A=83=F8_=BA=FF= +=FF=FF=FFu=E41=D2=EB=E0=90=8Dt&=00U=89=E5WVS=83=EC=1C=8BE=0C=8B]=08=8Bu=10= +=89E=F0=B8=00=80=00=00=89D$=04=89=1C$=E8"=A9=FF=FF=85=C0=89=C7=0F=84=10=02= +=00=00=89D$=08=B8=00=80=00=00=89D$=04=8BE=F0=89=04$=E8=00=A8=FF=FF=85=C0=0F= +=84=C0=00=00=00=E8=A3=A5=FF=FF=89E=E8=8B=00=83=F8&=89E=ECtq=83=F8_tl=89<$= +=E8=C9=A5=FF=FF=89t$=04=8BE=F0=89=04$=E8=AA=A5=FF=FF=8BE=F0=89=04$=E8=E7C= +=00=00=C7=04$=00=00=00=00=89=C3=B8=05=00=00=00=89D$=08=B83=87=05=08=89D$=04= +=E8=EF=A4=FF=FF=89D$=08=89\$=0C=8BE=EC=89D$=04=C7=04$=00=00=00=00=E8=14=A9= +=FF=FF=BA=FF=FF=FF=FF=83=C4=1C=89=D0[^_]=C3=89<$=E8=ED=A3=FF=FF=89<$=89=C3= +=E8S=A5=FF=FF=83=FB=03u=85=89t$=04=8BE=F0=89=04$=E8/=A5=FF=FF1=D2=85=C0t=CA= +=8BE=E8=8B=00=89E=EC=E9r=FF=FF=FF=8Dt&=00=89<$=E8 =A5=FF=FF=F7=C6=00=0E=00= +=00=0F=85=CC=00=00=00=81=E6=00=F0=00=00=81=FE=00@=00=00t=041=D2=EB=93=89=1C= +$=BE=00@=00=00=89t$=04=E8=01=A8=FF=FF=85=C0=89=C7t=7F=89D$=08=B9=00@=00=00= +=89L$=04=8BE=F0=89=04$=E8=E3=A6=FF=FF=85=C0u=0A=89<$=E8=C7=A4=FF=FF=EB=BF= +=8BE=F0=89=04$=E8=F2B=00=00=C7=04$=00=00=00=00=89=C6=BA=05=00=00=00=89T$=08= +=B83=87=05=08=89D$=04=E8=FA=A3=FF=FF=89=C3=E8S=A4=FF=FF=89t$=0C=89\$=08=8B= +=00=C7=04$=00=00=00=00=89D$=04=E8=19=A8=FF=FF=89<$=E8q=A4=FF=FF=E9=F8=FE=FF= +=FF=89=1C$=E8=9CB=00=00=89=C3=E8=1D=A4=FF=FF=89\$=0C=BBN=87=05=08=89\$=08= +=8B=00=E9=C5=FE=FF=FF=90=89t$=04=8BE=F0=89=04$=E8)=A4=FF=FF=85=C0=0F=84=1D= +=FF=FF=FF=8BE=F0=BF3=87=05=08=89=04$=E8YB=00=00=89|$=04=89=C6=B8=05=00=00= +=00=89D$=08=C7=04$=00=00=00=00=E8f=A3=FF=FF=89=C3=E8=BF=A3=FF=FF=89t$=0C=EB= +=A5=90=E8=B3=A3=FF=FF=89=C7=8B=00=83=F8&t=05=83=F8_u=15=89u=0C=8BE=F0=89E= +=08=83=C4=1C[^_]=E9(=00=00=00=89=1C$=E8=00B=00=00=89D$=0C=B8N=87=05=08=89= +D$=08=8B=07=E90=FE=FF=FF=8D=B6=00=00=00=00=8D=BF=00=00=00=00U=89=E5WVS=83= +=ECL=A1Q=87=05=08=8B]=0C=89E=C8=A1U=87=05=08=F6=C7=01=89E=CC=A1Y=87=05=08= +=89E=D0=A1]=87=05=08=89E=D4=A1a=87=05=08=89E=D8=0F=B6=05e=87=05=08=88E=DC= +t=04=C6E=CBr=84=DBy=04=C6E=CCw=F6=C3@t=04=C6E=CDx=F6=C3 t=04=C6E=D2r=F6=C3= +=10t=04=C6E=D3w=F6=C3=08t=04=C6E=D4x=F6=C3=04t=04=C6E=D9r=F6=C3=02t=04=C6= +E=DAw=F6=C3=01t=04=C6E=DBx=8DE=C8=89=04$=E8=7F=A5=FF=FF=85=C0=89=C6=0F=84= +T=01=00=00=89D$=08=B8=00=80=00=00=89D$=04=8BE=08=89=04$=E8=FD=A4=FF=FF=85= +=C0=0F=84=91=00=00=00=E8=A0=A2=FF=FF=89=C7=8B=00=894$=89E=C4=E8=D1=A2=FF=FF= +=8B=07=83=F8_tZ=83=F8&tU=8BE=08=89=04$=E8=F2@=00=00=C7=04$=00=00=00=00=89= +=C3=B8=05=00=00=00=89D$=08=B8f=87=05=08=89D$=04=E8=FA=A1=FF=FF=89D$=08=8B= +E=C4=89\$=0C=89D$=04=C7=04$=00=00=00=00=E8=1F=A6=FF=FF=BA=FF=FF=FF=FF=83=C4= +L=89=D0[^_]=C3=89\$=04=8BE=08=89=04$=E8Q=A2=FF=FF1=D2=85=C0t=E1=8B?=89}=C4= +=EB=8F=894$=E8L=A2=FF=FF=F6=C7=0Eub=81=E3=00=F0=00=00=81=FB=00@=00=00t=04= +1=D2=EB=BB=8BE=08=89=04$=E8j=A5=FF=FF=85=C0t=ED=8BE=08=89=04$=E8S@=00=00=B9= +=05=00=00=00=BAf=87=05=08=89=C6=89L$=08=89T$=04=C7=04$=00=00=00=00=E8[=A1= +=FF=FF=89=C3=E8=B4=A1=FF=FF=89t$=0C=89\$=08=8B=00=E9V=FF=FF=FF=89\$=04=8B= +E=08=89=04$=E8=C6=A1=FF=FF=85=C0t=8B=8BE=08=BF=05=00=00=00=BB3=87=05=08=89= +=04$=E8=F5?=00=00=89|$=08=89=C6=89\$=04=EB=AA=8BE=08=89=04$=E8=DE?=00=00=89= +=C3=E8_=A1=FF=FF=89\$=0C=BAN=87=05=08=89T$=08=EB=A4=90=90=90=90=90=90=90=90= +U=89=E5WVS=83=EC=0C=8B]=10=89=D8K=85=C0u=0D=8BE=08=83=C4=0C[^_]=C3=89=F6=E8= +=13=A5=FF=FF=89=C7=89=F6=8D=BC'=00=00=00=00=8BE=0C=0F=B6=14=03=8B=07=0F=B6= +=F2=F6Dp=01=01t=1D=E8 =A0=FF=FF=8B=00=0F=B6=04=B0=8BU=08=88=04=13=89=D8K=85= +=C0u=D5=EB=B6=8Dv=00=88=D0=EB=EA=8D=B6=00=00=00=00=8D=BF=00=00=00=00U=89=E5= +WVS=83=EC=0C=8B]=10=89=D8K=85=C0u=0D=8BE=08=83=C4=0C[^_]=C3=89=F6=E8=A3=A4= +=FF=FF=89=C7=89=F6=8D=BC'=00=00=00=00=8BE=0C=0F=B6=14=03=8B=07=0F=B6=F2=F6= +Dp=01=02t=1D=E8=80=A2=FF=FF=8B=00=0F=B6=04=B0=8BU=08=88=04=13=89=D8K=85=C0= +u=D5=EB=B6=8Dv=00=88=D0=EB=EA=8D=B6=00=00=00=00=8D=BF=00=00=00=00U1=C9=89= +=E5WVS=81=EC=EC=04=00=00=8BE=14=89=8D=90=FB=FF=FF=8BU=08=8BM=14=8B@=08=89= +=95=8C=FB=FF=FF=8BI(1=D2=89=85=98=FB=FF=FF=83=F8=0C=89=95=84=FB=FF=FF=89=8D= +=94=FB=FF=FF=0F=8E=EA=16=00=00=83=E8=0C=89=85=98=FB=FF=FF=8BE=10=89=85=88= +=FB=FF=FF=0F=B6=00=84=C0=0F=84=AD=00=00=00=88=C11=C01=DB1=F6=89=85=80=FB=FF= +=FF=BF=FF=FF=FF=FF=0F=BE=C1=89=9Dh=FB=FF=FF1=DB=83=F8?=89=BDp=FB=FF=FF=89= +=B5l=FB=FF=FF=0F=8Fz=16=00=00=83=F8&}%=83=F8#=0F=8F=F8=01=00=00=83=F8 }=17= +=83=E8=08=83=F8=05=0F=87=8B=00=00=00=8Dt&=00=8D=BC'=00=00=00=00=8B=95=90=FB= +=FF=FF=8BE=0C)=D01=D2=83=F8=01v^=8B=BD=8C=FB=FF=FF=85=FFt=0F=8B=85=8C=FB=FF= +=FF=88=08@=89=85=8C=FB=FF=FF=FF=85=90=FB=FF=FF=FF=85=88=FB=FF=FF=8B=95=88= +=FB=FF=FF=0F=B6=02=84=C0=88=C1=0F=85[=FF=FF=FF=8D=B6=00=00=00=00=8B=95=8C= +=FB=FF=FF=85=D2t=10=8BE=0C=85=C0t=09=8B=8D=8C=FB=FF=FF=C6=01=00=8B=95=90=FB= +=FF=FF=81=C4=EC=04=00=00=89=D0[^_]=C3=8Dv=00=8B=B5=84=FB=FF=FF1=DB=A1=9C=A8= +=05=08=8B=15=A0=A8=05=08=85=F6=89=85=A0=FB=FF=FF=89=95=A4=FB=FF=FF=0F=84=12= +=01=00=00=8B=BD=84=FB=FF=FF=8B=8D=88=FB=FF=FF)=CF=8Dt&=00=8D=BC'=00=00=00= +=00=C7=04$=00=00=00=00=8B=B5=88=FB=FF=FF=89=F8)=D8=8D=95=A0=FB=FF=FF=89T$= +=0C=01=DE=89D$=08=89t$=04=E8=EE=9F=FF=FF=85=C0t&=83=F8=FE=0F=84=B3=00=00=00= +=83=F8=FF=0F=84=A4=00=00=00=8D=8D=A0=FB=FF=FF=01=C3=89=0C$=E8=08=A2=FF=FF= +=85=C0t=AC=89=DE=8B=95=90=FB=FF=FF=F7=D6=89=F0=C1=E8=1FH!=F0=8D<=18=8BE=0C= +)=D01=D29=C7=0F=838=FF=FF=FF=8B=85=8C=FB=FF=FF=85=C0tG=85=F6~!=89t$=08=B8= + =00=00=00=89D$=04=8B=85=8C=FB=FF=FF=89=04$=E8=87=A1=FF=FF=01=B5=8C=FB=FF= +=FF=89\$=08=8B=95=88=FB=FF=FF=8B=8D=8C=FB=FF=FF=89T$=04=89=0C$=E8=F5=9F=FF= +=FF=01=9D=8C=FB=FF=FF=01=BD=90=FB=FF=FF=8B=85=88=FB=FF=FF=8D\=03=FF=89=9D= +=88=FB=FF=FF=E9=8D=FE=FF=FFC=E9j=FF=FF=FF=894$=E8=C6=9E=FF=FF=01=C3=E9[=FF= +=FF=FF=8B=95=88=FB=FF=FF=89=14$=E8=B1=9E=FF=FF=8B=8D=88=FB=FF=FF=8DD=08=01= +=89=85=84=FB=FF=FF=E9=CB=FE=FF=FF=83=F8%=0F=85=9B=FE=FF=FF=8Dt&=00=8D=BC'= +=00=00=00=00=FF=85=88=FB=FF=FF=8B=95=88=FB=FF=FF=0F=B6=0A=0F=BE=D1=88=C8=83= +=FA0t=17=83=FA0=0F=8F!=14=00=00=83=FA#=0F=84=0E=14=00=00=83=FA-u=0B=0F=BE= +=C0=89=85=80=FB=FF=FF=EB=C5=0F=BE=C0=83=E80=83=F8=09wX1=C0=89=85p=FB=FF=FF= +=81=BDp=FB=FF=FF=CC=CC=CC=0C=0F=8F=CB=13=00=00=81=BDp=FB=FF=FF=CC=CC=CC=0C= +=0F=84=AC=13=00=00=8B=85p=FB=FF=FF=8D=14=80=0F=BE=C1=8DTP=D0=89=95p=FB=FF= +=FF=FF=85=88=FB=FF=FF=8B=95=88=FB=FF=FF=0F=B6=0A=0F=BE=C1=83=E80=83=F8=09= +v=B0=0F=BE=C1=83=F8E=0F=84V=13=00=001=F6=83=F8O=89=B5|=FB=FF=FF=0F=84E=13= +=00=00=0F=BE=D1=83=FAz=89=95d=FB=FF=FF=0F=87=B3=00=00=00=FF$=95=9C=87=05=08= +=8B=9D|=FB=FF=FF=85=DB=0F=85=9E=00=00=00=8B=9Dp=FB=FF=FF=8B=95=90=FB=FF=FF= +K=89=D8=C1=E8=1FH!=D8=8Dp=01=8BE=0C)=D01=D29=C6=0F=83w=FD=FF=FF=8B=85=8C=FB= +=FF=FF=85=C0tF=85=DB~3=83=BD=80=FB=FF=FF0tD=89\$=08=8B=95=8C=FB=FF=FF=B8 = +=00=00=00=89D$=04=89=14$=E8=BD=9F=FF=FF=01=9D=8C=FB=FF=FF=8B=85=88=FB=FF=FF= +=0F=B6=08=8B=95=8C=FB=FF=FF=88=0AB=89=95=8C=FB=FF=FF=01=B5=90=FB=FF=FF=E9= +=DD=FC=FF=FF=89\$=08=8B=8D=8C=FB=FF=FF=B80=00=00=00=89D$=04=89=0C$=EB=BA=8D= +t&=00=8B=95=88=FB=FF=FF=BE=01=00=00=00=80:%t=0F=8B=85=88=FB=FF=FFF)=F0=80= +x=01%u=F1=8B=9Dp=FB=FF=FF=8B=95=90=FB=FF=FF)=F3=89=D8=C1=E8=1FH!=D8=8D<0=8B= +E=0C)=D01=D29=C7=0F=83=B9=FC=FF=FF=8B=85=8C=FB=FF=FF=85=C0t]=85=DB~*=83=BD= +=80=FB=FF=FF0t|=89\$=08=B8 =00=00=00=89D$=04=8B=85=8C=FB=FF=FF=89=04$=E8=FF= +=9E=FF=FF=01=9D=8C=FB=FF=FF=8B=8Dh=FB=FF=FF=85=C9t0=89t$=08=8B=85=88=FB=FF= +=FF)=F0@=89D$=04=8B=95=8C=FB=FF=FF=89=14$=E8=B8=FA=FF=FF=01=B5=8C=FB=FF=FF= +=01=BD=90=FB=FF=FF=E9=08=FC=FF=FF=89t$=08=8B=85=88=FB=FF=FF=8B=8D=8C=FB=FF= +=FF)=F0@=89D$=04=89=0C$=E80=9D=FF=FF=EB=CE=89\$=08=8B=8D=8C=FB=FF=FF=B80=00= +=00=00=89D$=04=89=0C$=EB=82=FF=8D=88=FB=FF=FF=E9=03=FF=FF=FF=8B=BD|=FB=FF= +=FF=85=FF=0F=85=F5=FE=FF=FF=85=DBt=13=BE=01=00=00=001=DB=89=B5h=FB=FF=FF=89= +=9Dl=FB=FF=FF=C6=85=9C=FB=FF=FF%=8B=9D|=FB=FF=FF=8D=85=9D=FB=FF=FF=85=DBt= +=13=0F=B6=8D|=FB=FF=FF=8D=85=9E=FB=FF=FF=88=8D=9D=FB=FF=FF=0F=B6=95d=FB=FF= +=FF=8D=BD=D8=FB=FF=FF=C6@=01=00=88=10=8BM=14=8D=85=9C=FB=FF=FF=89D$=08=89= +<$=89L$=0C=B9=00=04=00=00=89L$=04=E8=17=9B=FF=FF=85=C0=89=C6u=0D=80=BD=D8= +=FB=FF=FF=00=0F=85=FA=00=00=00=8B=9Dp=FB=FF=FF=8B=95=90=FB=FF=FF)=F3=89=D8= +=C1=E8=1FH!=D8=8D=040=89=85\=FB=FF=FF=8BE=0C)=D01=D29=85\=FB=FF=FF=0F=83:= +=FB=FF=FF=8B=85=8C=FB=FF=FF=85=C0tb=85=DB~.=83=BD=80=FB=FF=FF0=0F=84=92=00= +=00=00=89\$=08=8B=95=8C=FB=FF=FF=B8 =00=00=00=89D$=04=89=14$=E8|=9D=FF=FF= +=01=9D=8C=FB=FF=FF=8B=85l=FB=FF=FF=85=C0uO=8B=85h=FB=FF=FF=85=C0t-=89t$=08= +=8B=85=8C=FB=FF=FF=89|$=04=89=04$=E84=F9=FF=FF=01=B5=8C=FB=FF=FF=8B=8D\=FB= +=FF=FF=01=8D=90=FB=FF=FF=E9~=FA=FF=FF=89t$=08=8B=95=8C=FB=FF=FF=89|$=04=89= +=14$=E8=AF=9B=FF=FF=EB=D1=89t$=08=8B=8D=8C=FB=FF=FF=89|$=04=89=0C$=E8=7F=F8= +=FF=FF=EB=B9=89\$=08=B80=00=00=00=89D$=04=8B=85=8C=FB=FF=FF=89=04$=E9i=FF= +=FF=FF1=D2=E9k=FA=FF=FF=83=BD|=FB=FF=FFO=0F=84^=FD=FF=FF=83=BD|=FB=FF=FFE= +=0F=84s=FE=FF=FF=8BE=14=8B=BDp=FB=FF=FF=8BH=14=81=C1l=07=00=00=83=FF=01=0F= +=8C=82=02=00=00=89=C8=BA=1F=85=EBQ=F7=EA=89=C8=C1=F8=1F=C1=FA=05)=C2=8D=04= +=92=8D=04=80=C1=E0=02)=C1=C1=E9=1F)=CA=89=D1=83=BD|=FB=FF=FFO=0F=84C=02=00= +=00=89=CA=89=CB=C1=EA=1F=89=95x=FB=FF=FF=8Du=E4t=02=F7=DB=B9=CD=CC=CC=CC=89= +=D8N=F7=E1=C1=EA=03=8D=04=92=01=C0)=C3=88=D8=040=89=D3=85=D2=88=06u=E5=8B= +=8Dx=FB=FF=FF=85=C9t=04N=C6=06-=83=BD=80=FB=FF=FF-=0F=84=E6=01=00=00=8DU=D8= +=89=F0)=D0=89=95@=FB=FF=FF=8D\8=F4=85=DB=0F=8E=86=00=00=00=83=BD=80=FB=FF= +=FF_=0F=84Y=01=00=00=8B=95=90=FB=FF=FF=8BE=0C)=D01=D29=C7=0F=83z=F9=FF=FF= +=8B=85x=FB=FF=FF=85=C0t!=8B=85=8C=FB=FF=FFF=85=C0t=10=8B=85=8C=FB=FF=FF=C6= +=00-@=89=85=8C=FB=FF=FF=FF=85=90=FB=FF=FF=8B=85=8C=FB=FF=FF=85=C0t!=89\$=08= +=8B=95=8C=FB=FF=FF=BF0=00=00=00=89|$=04=89=14$=E8=A2=9B=FF=FF=01=9D=8C=FB= +=FF=FF=01=9D=90=FB=FF=FF1=C9=89=8Dp=FB=FF=FF)=B5@=FB=FF=FF=8B=9Dp=FB=FF=FF= +=8B=95=90=FB=FF=FF=8B=BD@=FB=FF=FF=83=C7=0C)=FB=89=D8=C1=E8=1FH!=D8=8D=04= +8=89=85X=FB=FF=FF=8BE=0C)=D01=D29=85X=FB=FF=FF=0F=83=D5=F8=FF=FF=8B=85=8C= +=FB=FF=FF=85=C0tT=85=DB~*=83=BD=80=FB=FF=FF0tp=89\$=08=8B=95=8C=FB=FF=FF=B8= + =00=00=00=89D$=04=89=14$=E8=1B=9B=FF=FF=01=9D=8C=FB=FF=FF=8B=85h=FB=FF=FF= +=85=C0t-=89|$=08=8B=8D=8C=FB=FF=FF=89t$=04=89=0C$=E8=DD=F6=FF=FF=01=BD=8C= +=FB=FF=FF=8B=95X=FB=FF=FF=01=95=90=FB=FF=FF=E9'=F8=FF=FF=89|$=08=8B=85=8C= +=FB=FF=FF=89t$=04=89=04$=E8X=99=FF=FF=EB=D1=89\$=08=B80=00=00=00=89D$=04=8B= +=85=8C=FB=FF=FF=89=04$=EB=8E=8B=95=90=FB=FF=FF=8BE=0C)=D01=D29=C3=0F=83!=F8= +=FF=FF=8B=BD=8C=FB=FF=FF=85=FFt!=89\$=08=B9 =00=00=00=89L$=04=8B=8D=8C=FB= +=FF=FF=89=0C$=E8t=9A=FF=FF=01=9D=8C=FB=FF=FF=01=9D=90=FB=FF=FF1=C0=8B=95p= +=FB=FF=FF)=DA9=9Dp=FB=FF=FF=0F=9E=C0H!=D0=89=85p=FB=FF=FF=E9=B9=FE=FF=FF=8D= +M=D8=89=8D@=FB=FF=FF=E9=AB=FE=FF=FF=85=C9=0F=89=DB=FB=FF=FF=E9=B0=FD=FF=FF= +=BF=01=00=00=00=E9t=FD=FF=FF=BE=81=87=05=08=89=B5t=FB=FF=FF=8B=85|=FB=FF=FF= +=85=C0=0F=85=91=FA=FF=FF=C7=04$=00=00=00=00=8BM=1C=8BU=14=8BE=18=8B=B5=8C= +=FB=FF=FF=89L$=14=8B=8Dt=FB=FF=FF=89T$=0C=89D$=10=89L$=08=B9=FF=FF=FF=FF=89= +L$=04=E8'=F6=FF=FF=8B=9Dp=FB=FF=FF=89=C7=8B=95=90=FB=FF=FF)=C3=89=D8=C1=E8= +=1FH!=D8=8D=048=89=85`=FB=FF=FF=8BE=0C)=D01=D29=85`=FB=FF=FF=0F=83#=F7=FF= +=FF=8B=85=8C=FB=FF=FF=85=C0tt=85=DB~.=83=BD=80=FB=FF=FF0=0F=84=BE=00=00=00= +=89\$=08=8B=95=8C=FB=FF=FF=B9 =00=00=00=89L$=04=89=14$=E8e=99=FF=FF=01=9D= +=8C=FB=FF=FF=8BE=18=8BU=14=8BM=1C=89D$=10=8BE=0C=89T$=0C=8B=95=90=FB=FF=FF= +=89L$=14=8B=8Dt=FB=FF=FF)=D0=89D$=04=8B=85=8C=FB=FF=FF=89L$=08=89=04$=E8{= +=F5=FF=FF=01=BD=8C=FB=FF=FF=8B=BDh=FB=FF=FF=8B=95`=FB=FF=FF=01=95=90=FB=FF= +=FF=85=FF=0F=84L=F6=FF=FF;=B5=8C=FB=FF=FF=0F=83@=F6=FF=FF=E8=A2=99=FF=FF=89= +=C7=0F=B6=16=8B=07=0F=B6=DA=F6DX=01=02t=1B=E8=8C=97=FF=FF=8B=00=0F=B6=04=98= +=88=06F;=B5=8C=FB=FF=FFr=DB=E9=0F=F6=FF=FF=88=D0=EB=EC=89\$=08=B80=00=00=00= +=89D$=04=8B=85=8C=FB=FF=FF=89=04$=E9=3D=FF=FF=FF=B8=8A=87=05=08=89=85t=FB= +=FF=FF=E9=80=FE=FF=FF=83=BD|=FB=FF=FFE=0F=84=12=F9=FF=FF=8B=BDp=FB=FF=FF=83= +=FF=02|=0B=8BE=14=8BH=08=E9=EB=FB=FF=FF=BF=02=00=00=00=EB=EE=90=8Dt&=00=83= +=BD|=FB=FF=FFE=0F=84=E3=F8=FF=FF=8B=BDp=FB=FF=FF=83=FF=02|=0B=8B=8D=98=FB= +=FF=FF=E9=BC=FB=FF=FF=BF=02=00=00=00=EB=EE=8D=B6=00=00=00=00=83=BD|=FB=FF= +=FFE=0F=84=B3=F8=FF=FF=8B=BDp=FB=FF=FF=83=FF=02|=0B=8BU=14=8BJ=04=E9=8C=FB= +=FF=FF=BF=02=00=00=00=EB=EE=8D=B6=00=00=00=00=83=BD|=FB=FF=FFE=0F=84=83=F8= +=FF=FF=83=BDp=FB=FF=FF=FF=8BM=1Ct&=8B=9Dp=FB=FF=FF=83=FB=08=7F=1B=BEgfff=89= +=C8C=F7=EE=89=C8=C1=F8=1F=C1=FA=02=89=D1)=C1=83=FB=08~=EA=8B=BDp=FB=FF=FF= +=83=FF=09=0F=8D1=FB=FF=FF=BF=09=00=00=00=E9'=FB=FF=FF=B8=01=00=00=00=89=85= +l=FB=FF=FF=B8p=00=00=00=89=85d=FB=FF=FF=85=DB=0F=84<=F9=FF=FF1=C0=89=85h=FB= +=FF=FF=B8=01=00=00=00=89=85l=FB=FF=FF=E9$=F9=FF=FF=B8r]=05=08=89=85t=FB=FF= +=FF=E9a=FD=FF=FF=83=BD|=FB=FF=FFE=0F=84=E5=F7=FF=FF=8B=BDp=FB=FF=FF=83=FF= +=02|=0A=8BU=14=8B=0A=E9=BF=FA=FF=FF=BF=02=00=00=00=EB=EF=B9=93=87=05=08=89= +=8Dt=FB=FF=FF=E9(=FD=FF=FF=83=BD|=FB=FF=FFE=0F=84=AC=F7=FF=FF=8B=BDp=FB=FF= +=FF=83=FF=02|)=8BM=14=8BQ=18=8BA=1C)=D0=8DX=07=BA=93$I=92=89=D8=F7=EA=8D=0C= +=13=89=D8=C1=F9=02=C1=F8=1F)=C1=E9g=FA=FF=FF=BF=02=00=00=00=EB=D0=90=83=BD= +|=FB=FF=FFE=0F=84c=F7=FF=FF=8B=BDp=FB=FF=FF=83=FF=02|:=8BU=14=BE=93$I=92=8B= +Z=18=83=C3=06=89=D8=F7=EE=89=D8=C1=F8=1F=01=DA=C1=FA=02)=C2=8D=04=D5=00=00= +=00=00)=D0=8BU=14)=C3=8BB=1C)=D8=8DX=07=89=D8=F7=EE=EB=94=BF=02=00=00=00=EB= +=BF=8D=B4&=00=00=00=00=83=BD|=FB=FF=FFO=0F=85%=F8=FF=FF=E9=FE=F6=FF=FF=83= +=BD|=FB=FF=FFE=0F=84=13=F8=FF=FF=83=BD|=FB=FF=FFO=0F=84=E4=F6=FF=FF=8B=BD= +p=FB=FF=FF=83=FF=01|=11=8BE=14=8BH=14=81=C1l=07=00=00=E9=B7=F9=FF=FF=BF=01= +=00=00=00=EB=E8=90=85=DBt=131=FF=BE=01=00=00=00=89=BDh=FB=FF=FF=89=B5l=FB= +=FF=FF=8B=9D=94=FB=FF=FF=85=DB=0F=84=F3=00=00=00=8B=95=94=FB=FF=FF=89=14$= +=E8=95=93=FF=FF=8B=9Dp=FB=FF=FF=89=C6=8B=95=90=FB=FF=FF)=C3=89=D8=C1=E8=1F= +H!=D8=8D<0=8BE=0C)=D01=D29=C7=0F=83c=F3=FF=FF=8B=85=8C=FB=FF=FF=85=C0=0F=84= +=03=F7=FF=FF=85=DB~.=83=BD=80=FB=FF=FF0=0F=84=81=00=00=00=89\$=08=B8 =00=00= +=00=89D$=04=8B=85=8C=FB=FF=FF=89=04$=E8=A1=95=FF=FF=01=9D=8C=FB=FF=FF=8B=85= +l=FB=FF=FF=85=C0u5=8B=9Dh=FB=FF=FF=85=DBt=0F=89t$=08=8B=85=94=FB=FF=FF=E9= +=96=F6=FF=FF=89t$=08=8B=8D=94=FB=FF=FF=8B=85=8C=FB=FF=FF=89L$=04=89=04$=E9= +=B7=F6=FF=FF=89t$=08=8B=95=94=FB=FF=FF=8B=8D=8C=FB=FF=FF=89T$=04=89=0C$=E8= +=B8=F0=FF=FF=E9k=F6=FF=FF=89\$=08=8B=8D=8C=FB=FF=FF=B80=00=00=00=89D$=04=89= +=0C$=E9z=FF=FF=FF=B9E\=05=08=89=8D=94=FB=FF=FF=E9=FD=FE=FF=FF=83=BD|=FB=FF= +=FFE=0F=84=8B=F5=FF=FF=8B=BDp=FB=FF=FF=83=FF=02|=0B=8BU=14=8BJ=0C=E9d=F8=FF= +=FF=BF=02=00=00=00=EB=EE=83=BD|=FB=FF=FFE=0F=84a=F5=FF=FF=8B=BDp=FB=FF=FF= +=83=FF=02|0=8BE=14=8BH=0C=83=BD=80=FB=FF=FF0=0F=842=F8=FF=FF=83=BD=80=FB=FF= +=FF-=0F=84%=F8=FF=FF=BB_=00=00=00=89=9D=80=FB=FF=FF=E9=15=F8=FF=FF=BF=02=00= +=00=00=EB=C9=83=BD|=FB=FF=FFE=0F=84=12=F5=FF=FF=8BU=14=8Bz=14=8BZ=1C=8BR=18= +=8D=87l=07=00=00=89=85T=FB=FF=FF=89=D8)=D0=89=95L=FB=FF=FF=8D=B0~=01=00=00= +=89=F0=BA=93$I=92=F7=EA=89=F0=C1=F8=1F=01=F2=C1=FA=02)=C2=8D=04=D5=00=00=00= +=00)=D0)=C6=89=D8)=F0=83=C0=03=89=85P=FB=FF=FF=0F=88=B0=01=00=001=C0=F6=85= +T=FB=FF=FF=03=89=85D=FB=FF=FFue=8B=85T=FB=FF=FF=BE=1F=85=EBQ=F7=EE=8B=85T= +=FB=FF=FF=89=D7=C1=FA=05=89=954=FB=FF=FF=99)=954=FB=FF=FF=8B=854=FB=FF=FF= +=8D=04=80=8D=04=80=89=85(=FB=FF=FF=C1=E0=029=85T=FB=FF=FFu=18=89=F8=C1=F8= +=07)=D0=8D=04=80=8D=04=80=C1=E0=049=85T=FB=FF=FFu=0B=BF=01=00=00=00=89=BD= +D=FB=FF=FF=8B=B5D=FB=FF=FF=8B=95L=FB=FF=FF)=F3=81=EBm=01=00=00=89=D8)=D0=8D= +=B0~=01=00=00=B8=93$I=92=F7=EE=89=F0=C1=F8=1F=8D=142=C1=FA=02)=C2=8D=04=D5= +=00=00=00=00)=D0)=C6)=F3=89=D8=83=C0=03x=0C=FF=85T=FB=FF=FF=89=85P=FB=FF=FF= +=0F=BE=C1=83=F8G=0F=84=BF=00=00=00=83=F8gt=3D=8B=BDp=FB=FF=FF=83=FF=02|+=8B= +=85P=FB=FF=FF=BA=93$I=92=F7=EA=8B=85P=FB=FF=FF=01=C2=8B=85P=FB=FF=FF=C1=FA= +=02=C1=F8=1F)=C2=8DJ=01=E9=97=F6=FF=FF=BF=02=00=00=00=EB=CE=8B=BDp=FB=FF=FF= +=83=FF=02|k=8B=85T=FB=FF=FF=BB=1F=85=EBQ=F7=EB=8B=85T=FB=FF=FF=89=95,=FB=FF= +=FF=8B=8D,=FB=FF=FF=99=C1=F9=05)=D1=8D=04=89=8D=04=80=C1=E0=02)=85T=FB=FF= +=FF=8B=B5T=FB=FF=FF=83=C6d=89=F0=89=F1=F7=EB=89=95,=FB=FF=FF=8B=9D,=FB=FF= +=FF=89=85(=FB=FF=FF=89=F0=99=C1=FB=05)=D3=8D=04=9B=8D=04=80=C1=E0=02=E9=AC= +=FB=FF=FF=BF=02=00=00=00=EB=8E=8B=BDp=FB=FF=FF=83=FF=01|=0B=8B=8DT=FB=FF=FF= +=E9=FD=F5=FF=FF=BF=01=00=00=00=EB=EE=81=C7k=07=00=001=C0=89=BDT=FB=FF=FF=F6= +=85T=FB=FF=FF=03=89=85H=FB=FF=FFua=89=F8=BE=1F=85=EBQ=F7=EE=8B=85T=FB=FF=FF= +=89=D7=C1=FA=05=89=954=FB=FF=FF=99)=954=FB=FF=FF=8B=854=FB=FF=FF=8D=04=80= +=8D=04=80=89=85(=FB=FF=FF=C1=E0=029=85T=FB=FF=FFu=18=89=F8=C1=F8=07)=D0=8D= +=04=80=8D=04=80=C1=E0=049=85T=FB=FF=FFu=0B=BE=01=00=00=00=89=B5H=FB=FF=FF= +=8B=95H=FB=FF=FF=8D=9C=1Am=01=00=00=8B=95L=FB=FF=FF=89=D8)=D0=8D=B0~=01=00= +=00=B8=93$I=92=F7=EE=89=F0=C1=F8=1F=8D=142=C1=FA=02)=C2=8D=04=D5=00=00=00= +=00)=D0)=C6)=F3=83=C3=03=89=9DP=FB=FF=FF=E9N=FE=FF=FF=85=DBt=13=B8=01=00=00= +=00=89=85h=FB=FF=FF1=C0=89=85l=FB=FF=FF=8B=85|=FB=FF=FF=85=C0=0F=84:=F3=FF= +=FF=E9=13=F2=FF=FF=83=BD|=FB=FF=FFE=0F=84=06=F2=FF=FF=8B=BDp=FB=FF=FF=83=FF= +=03}=05=BF=03=00=00=00=8BE=14=8BH=1CA=E9=D9=F4=FF=FF=83=BD|=FB=FF=FFE=0F=84= +=DD=F1=FF=FF=8B=BDp=FB=FF=FF=83=FF=02|=0B=8BU=14=8BJ=08=E9w=FC=FF=FF=BF=02= +=00=00=00=EB=EE=83=BD|=FB=FF=FFE=0F=84=B3=F1=FF=FF=8B=BDp=FB=FF=FF=83=FF=02= +|=0B=8B=8D=98=FB=FF=FF=E9M=FC=FF=FF=BF=02=00=00=00=EB=EE=8D=B6=00=00=00=00= +=83=BD|=FB=FF=FFE=0F=84=83=F1=FF=FF=8B=BDp=FB=FF=FF=83=FF=02|=0B=8BE=14=8B= +H=10=E9}=FF=FF=FF=BF=02=00=00=00=EB=EE=8D=B6=00=00=00=00=8B=9Dp=FB=FF=FF1= +=D2=8B=BD=90=FB=FF=FFK=89=D8=C1=E8=1FH!=D8=8Dp=01=8BE=0C)=F89=C6=0F=839=EE= +=FF=FF=8B=8D=8C=FB=FF=FF=85=C9=0F=84=04=F1=FF=FF=85=DB~*=83=BD=80=FB=FF=FF= +0t6=89\$=08=8B=8D=8C=FB=FF=FF=B8 =00=00=00=89D$=04=89=0C$=E8{=90=FF=FF=01= +=9D=8C=FB=FF=FF=8B=85=8C=FB=FF=FF=C6=00=0A@=89=85=8C=FB=FF=FF=E9=C1=F0=FF= +=FF=89\$=08=BA0=00=00=00=89T$=04=8B=95=8C=FB=FF=FF=89=14$=EB=C8=FC=B8=0B=00= +=00=00=8Bu=14=89=C1=8D=BD=A8=FB=FF=FF=8D=85=A8=FB=FF=FF=F3=A5=89=04$=BBgf= +ff=8Du=E4=E8=C7H=00=00=89=C1=C1=E8=1F=89=85x=FB=FF=FF=89=C8=8B=BDx=FB=FF=FF= +=F7=EB=89=C8=C1=F8=1F=C1=FA=02)=C2=8D=04=92=01=C0)=C1=85=FF=89=C8=89=D1t=02= +=F7=D8N=040=85=D2=88=06=BF=01=00=00=00u=CD=E9=94=F3=FF=FF=8B=9Dp=FB=FF=FF= +=8B=95=90=FB=FF=FFK=89=D8=C1=E8=1FH!=D8=8Dp=01=8BE=0C)=D01=D29=C6=0F=83=3D= +=ED=FF=FF=8B=85=8C=FB=FF=FF=85=C0=0F=84=08=F0=FF=FF=85=DB~*=83=BD=80=FB=FF= +=FF0t/=89\$=08=B9 =00=00=00=89L$=04=8B=8D=8C=FB=FF=FF=89=0C$=E8=7F=8F=FF=FF= +=01=9D=8C=FB=FF=FF=8B=85=8C=FB=FF=FF=C6=00=09=E9=FF=FE=FF=FF=89\$=08=8B=95= +=8C=FB=FF=FF=BF0=00=00=00=89|$=04=89=14$=EB=CF=8B=BDp=FB=FF=FF=83=FF=01|0= +=8BU=14=B8=93$I=92=8BZ=18=83=C3=06=F7=EB=89=D8=C1=F8=1F=8D=14=1A=C1=FA=02= +)=C2=8D=04=D5=00=00=00=00)=D0)=C3=8DK=01=E9=8F=F2=FF=FF=BF=01=00=00=00=EB= +=C9=83=BD|=FB=FF=FFE=0F=84=8C=EF=FF=FF=8B=BDp=FB=FF=FF=83=FF=01|=0B=8BU=14= +=8BJ=18=E9e=F2=FF=FF=BF=01=00=00=00=EB=EE=83=BD|=FB=FF=FFE=0F=84=84=F0=FF= +=FF=8B=BDp=FB=FF=FF=83=FF=02|7=8BU=14=BB=1F=85=EBQ=8Br=14=89=F0=F7=EB=89=F0= +=C1=FA=05=89=95(=FB=FF=FF=99)=95(=FB=FF=FF=8B=95(=FB=FF=FF=8D=04=92=8D=04= +=80=C1=E0=02)=C6=E9=C4=FB=FF=FF=BF=02=00=00=00=EB=C2=8BU=14=8BJ =85=C9=0F= +=88=CC=EB=FF=FF=8Bz$=85=FF=0F=88=EA=00=00=00=8B=9Dp=FB=FF=FF=8B=95=90=FB=FF= +=FFK=89=D8=C1=E8=1FH!=D8=8Dp=01=8BE=0C)=D01=D29=C6=0F=83=D9=EB=FF=FF=8B=85= +=8C=FB=FF=FF=85=C0tB=85=DB~.=83=BD=80=FB=FF=FF0=0F=84=8D=00=00=00=89\$=08= +=8B=85=8C=FB=FF=FF=B9 =00=00=00=89L$=04=89=04$=E8=1B=8E=FF=FF=01=9D=8C=FB= +=FF=FF=8B=95=8C=FB=FF=FF=C6=02+B=89=95=8C=FB=FF=FF=01=B5=90=FB=FF=FF=89=F8= +=B9=89=88=88=88=F7=E9=89=F8=C1=F8=1F=8D4=17=8B=BDp=FB=FF=FF=C1=FE=05)=C6=83= +=FF=04}=05=BF=04=00=00=00=89=F0=F7=E9=89=F0=C1=F8=1F=01=F2=C1=FA=05)=C2=89= +=D0=8D=1C=92=C1=E0=04)=D0=8D=1C=9B=8D=14=85=00=00=00=00=89=F0)=D0=8D=0C=98= +=E9 =F1=FF=FF=89\$=08=8B=8D=8C=FB=FF=FF=B80=00=00=00=89D$=04=89=0C$=E9n=FF= +=FF=FF=8B=9Dp=FB=FF=FF=8B=95=90=FB=FF=FFK=89=D8=C1=E8=1FH!=D8=8Dp=01=8BE=0C= +)=D01=D29=C6=0F=83=EF=EA=FF=FF=8B=85=8C=FB=FF=FF=85=C0t>=85=DB~*=83=BD=80= +=FB=FF=FF0t>=89\$=08=8B=85=8C=FB=FF=FF=B9 =00=00=00=89L$=04=89=04$=E85=8D= +=FF=FF=01=9D=8C=FB=FF=FF=8B=95=8C=FB=FF=FF=C6=02-B=89=95=8C=FB=FF=FF=01=B5= +=90=FB=FF=FF=F7=DF=E9=13=FF=FF=FF=89\$=08=8B=8D=8C=FB=FF=FF=B80=00=00=00=89= +D$=04=89=0C$=EB=C0=89=F6=FF=85=88=FB=FF=FF=89=85|=FB=FF=FF=8B=85=88=FB=FF= +=FF=0F=B6=08=E9=A1=EC=FF=FF=0F=BE=C1=83=E80=83=F8=07=0F=8EE=EC=FF=FF=BF=FF= +=FF=FF=7F=89=BDp=FB=FF=FF=E9K=EC=FF=FF=BB=01=00=00=00=E9=BD=EB=FF=FF=83=FA= +^t=08=83=FA_=E9=DE=EB=FF=FF=B8=01=00=00=00=89=85h=FB=FF=FF=E9=A0=EB=FF=FF= +=83=F8A=0F=8C'=EA=FF=FF=83=F8_=0F=8E=9E=E9=FF=FF=83=E8a=83=F8=1D=E9=82=E9= +=FF=FF=8Dv=00=8B=85=98=FB=FF=FF=85=C0=0F=85=11=E9=FF=FF=B8=0C=00=00=00=E9= +=01=E9=FF=FF=90=90=90=90=90=90=90=90U=89=E5=8BE=08]=8B@=08=C3=90=8Dt&=00U= +=89=E5=8BE=08]=8B@=0C=C3=90=8Dt&=00U=89=E5=8BE=08]=8B@=10=C3=90=8Dt&=00U=89= +=E5=8BE=08V1=F6S=8B=08=8BX=049=D9sD=8D=B6=00=00=00=00=8D=BC'=00=00=00=00=8B= +=01=85=C0t*=8BA=04=BA=01=00=00=00=85=C0t=16=8D=B4&=00=00=00=00=8D=BC'=00=00= +=00=00=8B@=04B=85=C0u=F89=F2v=04=89=D6=89=F6=83=C1=089=D9r=C9[=89=F0^]=C3= +=8Dv=00U1=C9=89=E5W=8B}=08V1=F6S=8B=17=8B_=049=DAs)=8Dv=00=8D=BC'=00=00=00= +=00=8B=02=85=C0t=12=8BB=04FA=85=C0t=09=90=8B@=04A=85=C0u=F8=83=C2=089=DAr= +=E1;w=0Ct=071=C0[^_]=C3;O=10u=F4=B8=01=00=00=00=EB=EF=89=F6=8D=BC'=00=00=00= +=00U=89=E5WVS=83=EC=1C=8B]=08=89=1C$=E8=1C=FF=FF=FF=89=1C$=89=C7=E8=F2=FE= +=FF=FF=89=1C$=89=C6=E8=F8=FE=FF=FF=89E=F0=89=1C$=E8=0D=FF=FF=FF=89|$=08=89= +=C3=B8=9C=89=05=08=89D$=04=8BE=0C=89=04$=E8[=87=FF=FF=89t$=08=B8=B3=89=05= +=08=89D$=04=8BE=0C=89=04$=E8C=87=FF=FF=8BE=F01=D2R1=D2P=8BE=F0=DF,$=83=C4= +=08RV=DF,$=D9=C9=83=C4=08=D8=0D =8A=05=08=89D$=08=B8=00=8A=05=08=89D$=04=8B= +E=0C=DE=F1=89=04$=DD\$=0C=E8=03=87=FF=FF=89\$=08=B8=CA=89=05=08=89D$=04=8B= +E=0C=89=04$=E8=EB=86=FF=FF=83=C4=1C[^_]=C3=8Dt&=00=8D=BC'=00=00=00=00U=89= +=E5WVS=83=EC=0C=8Bu=08=8B}=0C=8BF=08=89<$=89D$=04=FFV=18=8B=16=8D=04=C2;F= +=04s?=8B=10=85=D2t+=85=C0=89=C3t%=8D=B4&=00=00=00=00=8D=BC'=00=00=00=00=8B= +=03=89<$=89D$=04=FFV=1C=84=C0u=11=8B[=04=85=DBu=E91=C0=83=C4=0C[^_]=C3=8B= +=03=EB=F4=E8n=87=FF=FF=8D=B6=00=00=00=00U=89=E5=83=EC=08=8BE=08=8BH=10=85= +=C9t!=8B=10=8BH=04=8Dt&=00=8D=BC'=00=00=00=009=CAs=11=8B=02=85=C0u=05=83=C2= +=08=EB=F1=C9=C31=C0=EB=FA=E8.=87=FF=FF=8D=B6=00=00=00=00U=89=E5VS=83=EC=10= +=8B]=08=8Bu=0C=8BC=08=894$=89D$=04=FFS=18=8B=13=8BK=04=8D=14=C29=CAsJ=85=D2= +=89=D0t=0E=8Dv=0090t2=8B@=04=85=C0u=F5=83=C2=089=CAs=1B=8D=B4&=00=00=00=00= +=8D=BC'=00=00=00=00=8B=02=85=C0u=09=83=C2=089=CAr=F31=C0=83=C4=10[^]=C3=8B= +@=04=85=C0t=CA=8B=00=EB=EE=E8=B2=86=FF=FF=8Dv=00=8D=BC'=00=00=00=00U1=C9=89= +=E5WVS=83=EC=04=8BE=08=8B}=10=8B=18=8B@=04=89E=F09=C3s3=8Dv=00=8B3=85=F6t= +=1E=89=DA=90=8D=B4&=00=00=00=009=F9s=1C=8B=02=8Bu=0C=8BR=04=89=04=8EA=85=D2= +u=EC=83=C3=08;]=F0r=D4=8Dt&=00[=89=C8[^_]=C3=90=8D=B4&=00=00=00=00U=89=E5= +W1=FFVS=83=EC=0C=8BE=08=8B0=8B@=049=C6sI=89=F6=8D=BC'=00=00=00=00=8B=16=85= +=D2t+=89=F3=90=8D=B4&=00=00=00=00=8BE=10=89D$=04=8B=03=89=04$=FFU=0C=84=C0= +t=1D=8B[=04G=85=DBu=E5=8BU=08=8BB=04=83=C6=089=C6r=C8=90=8D=B4&=00=00=00=00= +=83=C4=0C=89=F8[^_]=C3=8D=B6=00=00=00=00U=89=E5=8BM=08V=8Bu=0CS1=DB=0F=B6= +=11=84=D2t(=8D=B6=00=00=00=00=8D=BF=00=00=00=00=89=D8=0F=B6=D2=C1=E0=05)=D8= +A=01=D01=D2=F7=F6=0F=B6=01=84=C0=89=D3=88=C2u=E4=89=D8[^]=C3=8D=B4&=00=00= +=00=00=8D=BC'=00=00=00=00U=B9=03=00=00=00=89=E5W=8B}=08V=BE=09=00=00=009=FE= +Ss91=D2=89=F8=F7=F1=85=D2t/=EB=0D=90=90=90=90=90=90=90=90=90=90=90=90=90A= +=8D4=8EA9=FEs=171=D2=89=F8=F7=F1=85=D2u=ED=8D=B6=00=00=00=00=8D=BC'=00=00= +=00=00[=89=F81=D2=F7=F11=C0^_]=85=D2=0F=95=C0=C3=8D=B4&=00=00=00=00=8D=BC= +'=00=00=00=00U=89=E5S=83=EC=04=8B]=08=83=FB=09w=05=BB=0A=00=00=00=83=CB=01= +=89=F6=8D=BC'=00=00=00=00=89=1C$=E8h=FF=FF=FF=84=C0u=05=83=C3=02=EB=EFY=89= +=D8[]=C3=89=F6=8D=BC'=00=00=00=00U=A1=88=89=05=08=89=E5=8BU=08=89=02=A1=8C= +=89=05=08=89B=04=A1=90=89=05=08=89B=08=A1=94=89=05=08=89B=0C=A1=98=89=05=08= +=89B=10]=C3=90U=D9=EE=89=E5=8BU=08=8BJ=14=D9A=08=D9=C0=DD=E2=DF=E0=9Evy=D9= +=E8=DD=E1=DF=E0=DD=D9=9Evn=D9A=0C=DD=E9=DF=E0=9Evd=D9=01=D9=C0=DD=E4=DF=E0= +=DD=DC=9ErM=D9=C9=DD=E3=DF=E0=DD=DB=9EvL=D9A=04=DD=E1=DF=E0=9Ev8=D9=CB=DD= +=EB=DF=E0=DD=DA=9Er'=DA=E9=DF=E0=9Ev=12=B8=01=00=00=00=8Dt&=00=8D=BC'=00=00= +=00=00]=C3=C7B=14=88=89=05=081=C0=EB=F3=8Dv=00=DD=D8=DD=D8=EB=EC=DD=D8=90= +=8D=B4&=00=00=00=00=DD=D8=EB=EC=8D=B6=00=00=00=00=8D=BF=00=00=00=00U=89=E5= +WVS=83=EC=1C=8BE=10=8B}=08=8Bu=0C=85=C0=0F=84=F6=00=00=00=8B]=14=85=DB=0F= +=84=EB=00=00=00=C7=04$(=00=00=00=E8=A7=83=FF=FF=89=C31=C0=85=DB=0F=84=C0=00= +=00=00=85=F6=0F=84=D6=00=00=00=89s=14=89=1C$=E8=10=FF=FF=FF=84=C0=0F=84=AD= +=00=00=00=80~=10=00=0F=85=B2=00=00=001=D2RW=DF,$=83=C4=08=D8v=08=D9}=EE=0F= +=B7E=EEf=0D=00=0Cf=89E=EC=D9m=EC=DF}=E0=D9m=EE=8BE=E0=89=04$=E8^=FE=FF=FF= +=89C=08=C1=E0=03=89=04$=E88=83=FF=FF=89=03=85=C0=89=C2t]=8BC=08=8D=0C=C29= +=CA=89K=04=89=D0s=1D=89=CA=8D=B4&=00=00=00=00=C7=00=00=00=00=00=C7@=04=00= +=00=00=00=83=C0=089=D0r=EC=C7C=0C=00=00=00=00=8BE=10=C7C=10=00=00=00=00=C7= +C$=00=00=00=00=89C=18=8BE=14=89C=1C=8BE=18=89C =89=D8=83=C4=1C[^_]=C3=89=1C= +$=E8{=85=FF=FF=8Dv=001=C0=EB=E9=89=F8=E9o=FF=FF=FF=BE=88=89=05=08=E9 =FF=FF= +=FF=8Dt&=00=8D=BC'=00=00=00=00U=89=E5WVS=83=EC=0C=8B}=08=8B7=8BG=049=C6sR= +=8Dt&=00=8D=BC'=00=00=00=00=8B=16=85=D2t:=8B^=04=85=DBte=8BO =85=C9uR=C7=03= +=00=00=00=00=8BS=04=8BG$=85=D2=89C=04=89_$=89=D3u=E4=85=C9u-=C7F=04=00=00= +=00=00=8BG=04=C7=06=00=00=00=00=83=C6=089=C6r=B9=C7G=0C=00=00=00=00=C7G=10= +=00=00=00=00=83=C4=0C[^_]=C3=8B=06=89=04$=FF=D1=EB=CA=8B=03=89=04$=FF=D1=8B= +O =EB=A2=8BO =EB=B5=89=F6=8D=BC'=00=00=00=00U=89=E5WVS=83=EC=0C=8BE=08=89= +E=F0=8Bp =85=F6=0F=84=C6=00=00=00=8BX=10=85=DB=0F=84=BB=00=00=00=8B8=8B@=04= +9=C7sp=8B=0F=85=C9t$=85=FF=89=FBt=1E=8D=B6=00=00=00=00=8B=03=89=04$=8BM=F0= +=FFQ =8B[=04=85=DBu=EE=8BU=F0=8BB=04=83=C7=089=C7r=CF=8BM=F0=8B=119=C2=89= +=D7s4=8D=B6=00=00=00=00=8B_=04=85=DBt =89=F6=8D=BC'=00=00=00=00=8Bs=04=89= +=1C$=E8-=84=FF=FF=85=F6=89=F3u=EF=8BU=F0=8BB=04=83=C7=089=C7r=D2=8BM=F0=8B= +Y$=85=DBt=19=90=8D=B4&=00=00=00=00=8Bs=04=89=1C$=E8=FD=83=FF=FF=85=F6=89=F3= +u=EF=8BU=F0=8B=02=89=04$=E8=EA=83=FF=FF=8BM=F0=89M=08=83=C4=0C[^_]=E9=D8=83= +=FF=FF=8BE=F0=8B=10=8B@=04=E9w=FF=FF=FF=8Dv=00U=89=E5=83=EC=08=8BM=08=8BA= +$=85=C0t=10=89=C2=8B@=04=89A$=89=EC=89=D0]=C3=89=F6=C7=04$=08=00=00=00=E8= +=EC=80=FF=FF=89=C2=EB=E8U=89=E5=8BE=08=8BU=0C=8BH$=C7=02=00=00=00=00=89J=04= +=89P$]=C3=8D=B6=00=00=00=00U=89=E5WVS=83=EC=0C=8Bu=08=0F=B6E=14=8B}=0C=88= +E=F3=8BF=08=89<$=89D$=04=FFV=18=8B=16=8D=1C=C2;^=04=0F=83=A4=00=00=00=8BE= +=10=8B=13=89=181=C0=85=D2t4=89T$=04=89<$=FFV=1C=84=C0tF=80}=F3=00=8B;t=1C= +=8BK=04=85=C9u=1F=C7=03=00=00=00=00=EB=0D=90=90=90=90=90=90=90=90=90=90=90= +=90=90=89=F8=83=C4=0C[^_]=C3=8B=01=8BQ=04=89=03=89S=04=89L$=04=894$=E8P=FF= +=FF=FF=EB=DE=8BC=04=85=C0t!=8D=B4&=00=00=00=00=8B=00=89<$=89D$=04=FFV=1C=84= +=C0u=0E=8B[=04=8BC=04=85=C0u=E61=C0=EB=B4=80}=F3=00=8BS=04=8B:t=A7=8BB=04= +=89C=04=89T$=04=EB=B3=E8=1E=80=FF=FF=8D=B6=00=00=00=00U=89=E5WVS=83=EC,=8B= +U=08=8BB =89D$=10=8BB=1C=89D$=0C=8BB=18=89D$=08=8BB=14=89D$=04=8BE=0C=89=04= +$=E8=DD=FB=FF=FF=89=C71=C0=85=FF=0F=84=BE=00=00=00=8BM=08=8BA$=8B=11=89G$= +=8BA=04=89U=F09=C2su=8D=B6=00=00=00=00=8D=BF=00=00=00=00=8BU=F0=8B=0A=85=C9= +tR=89=D3=90=8Dt&=00=8B=0B=8BG=08=89M=E8=89D$=04=89=0C$=FFW=18=8B=17=8D4=C2= +;w=04=0F=83=B7=00=00=00=8BC=04=89E=EC=8B=06=85=C0=0F=84=85=00=00=00;]=F0t= +`=8BF=04=89C=04=89^=04=8B]=EC=85=DBu=BB=8BU=08=8BB=04=83E=F0=089E=F0r=9C=8B= +M=08=8B=11=89=14$=E8=C7=81=FF=FF=8BU=08=8B=07=89=02=8BG=04=89B=04=8BG=08=89= +B=08=8BG=0C=89B=0C=8BG$=89B$=89<$=E8=A0=81=FF=FF=B8=01=00=00=00=83=C4,[^_= +]=C3=89<$=E8=C3=FD=FF=FF=89=C21=C0=85=D2t=E8=8BF=04=8BM=E8=89B=04=89=0A=89= +V=04=EB=89=FFG=0C=8BE=E8;]=F0=89=06=0F=84x=FF=FF=FF=89\$=04=89<$=E8=BE=FD= +=FF=FF=E9g=FF=FF=FF=E8=CC~=FF=FF=8Dt&=00U=89=E5=83=EC(=89]=F8=8B]=0C=89u=FC= +=8Bu=08=85=DB=0F=84=19=01=00=00=89\$=041=C9=8DE=F4=89L$=0C=89D$=08=894$=E8= +=9D=FD=FF=FF=85=C0t=0A=8B]=F8=8Bu=FC=89=EC]=C3=8BE=F4=8B=10=85=D2=0F=85=BB= +=00=00=00=FFF=0C1=D2=89=18=FFF=10=8BF=0CR1=D2P=8BF=08=8BN=14=DF,$=83=C4=08= +RP=DF,$=83=C4=08=D8I=08=D9=C9=DA=E9=DF=E0=9Ew=04=89=D8=EB=B4=894$=E8=95=F9= +=FF=FF=8BF=0C1=D2R1=D2P=8BF=08=8BN=14=DF,$=83=C4=08RP=DF,$=83=C4=08=D9A=08= +=D9=C0=D8=CA=D9=CB=DD=EB=DF=E0=DD=DA=9EvF=80y=10=00t9=DD=D9=D8I=0C=D9}=F2= +=0F=B7E=F2=894$f=0D=00=0Cf=89E=F0=D9m=F0=DF}=E8=D9m=F2=8BE=E8=89D$=04=E8=C4= +=FD=FF=FF=84=C0=0F=94=C0=0F=B6=C0H!=C3=EB=86=D8I=0C=DE=C9=EB=C5=DD=D8=DD=D8= +=E9v=FF=FF=FF=894$=E8o=FC=FF=FF=89=C11=C0=85=C9=0F=84=1A=FF=FF=FF=FFF=10=8B= +U=F4=89=19=8BB=04=89A=04=89J=04=E9L=FF=FF=FF=E8=90}=FF=FF=90=8D=B4&=00=00= +=00=00U=B8=01=00=00=00=89=E5=83=EC(=89D$=0C=8DE=F4=89]=F8=8B]=08=89D$=08=8B= +E=0C=89u=FC=89=1C$=89D$=04=E8b=FC=FF=FF=89=C61=C0=85=F6t7=FFK=10=8BE=F4=8B= +=00=85=C0u)=FFK=0C1=D2=8BC=0CR1=D2P=8BC=08=8BK=14=DF,$=83=C4=08RP=DF,$=83= +=C4=08=D8=09=DA=E9=DF=E0=9Ew=0C=89=F0=8B]=F8=8Bu=FC=89=EC]=C3=89=1C$=E8a=F8= +=FF=FF=8BC=0C1=D2R1=D2P=8BC=08=8BK=14=DF,$=83=C4=08RP=DF,$=83=C4=08=D9=01= +=D8=C9=DD=EA=DF=E0=DD=D9=9Ev:=80y=10=00t,=D8I=04=D9}=F2=0F=B7E=F2=89=1C$f= +=0D=00=0Cf=89E=F0=D9m=F0=DF}=E8=D9m=F2=8BE=E8=89D$=04=E8=97=FC=FF=FF=EB=90= +=D8I=04=D8I=08=EB=CF=DD=D8=EB=84=90=90=90=90=90=90=90=90=90U=89=E5=83=EC=08= +=C7=04$=01=00=00=00=E8>=D1=FF=FF=89=EC]=C3=8Dv=00=8D=BC'=00=00=00=00U=89=E5= +WVS=83=EC=1C1=DB=C7E=F0=FF=FF=FF=FF=8BE=08=C7E=EC=00=00=00=00=89=04$=E8=A4= +|=FF=FF=89E=E8=8BU=0C=8B=02=85=C0=0F=84=9D=00=00=00=C7E=E4=00=00=00=00=90= +=8Dt&=00=8BU=08=8BE=E8=89T$=04=8BU=0C=89D$=08=8B=04=9A=89=04$=E8=FC|=FF=FF= +=85=C0uP=8BU=0C=8B=04=9A=89=04$=E8Z|=FF=FF;E=E8=89=DAt_=83}=F0=FFte=8B}=10= +=85=FFt"=FC=8Bu=F0=8BE=14=8BM=E4=8B}=10=0F=AF=C6=8Bu=10=01=CF=01=C6=8BE=14= +9=C0=89=C1=F3=A6t=0C=C7E=EC=01=00=00=00=90=8Dt&=00=8BE=0CC=8BU=14=01U=E4=8B= +=14=98=85=D2=0F=85{=FF=FF=FF=8BE=EC=BA=FE=FF=FF=FF=85=C0u=03=8BU=F0=83=C4= +=1C=89=D0[^_]=C3=89=F6=89]=F0=EB=CB=8Dt&=00=8D=BC'=00=00=00=00U=89=E5=83=EC= +(=83}=10=FF=89]=F8=89u=FCt~=B8=05=00=00=00=89D$=08=B8$=8A=05=08=89D$=04=C7= +=04$=00=00=00=00=E8xz=FF=FF=C7=04$=01=00=00=00=89=C6=8BE=08=89D$=04=E8=0B= +=19=00=00=C7=04$=00=00=00=00=89=C3=8BE=0C=89D$=08=B8=05=00=00=00=89D$=04=E8= +]#=00=00=89D$=0C1=C0=89\$=10=89t$=08=89D$=04=C7=04$=00=00=00=00=E8g~=FF=FF= +=8B]=F8=8Bu=FC=89=EC]=C3=90=8Dt&=00=BA=05=00=00=00=B8A=8A=05=08=89T$=08=EB= +=80U=89=E5WVS=83=EC,=8BE=08=C7E=E0=00=00=00=00=8BU=0C=8BM=10=89E=F0=B8=05= +=00=00=00=89U=EC=89M=E8=89D$=08=B8\=8A=05=08=89D$=04=C7=04$=00=00=00=00=E8= +=C8y=FF=FF=89D$=04=A1d=A6=05=08=89=04$=E8=97y=FF=FF=C7E=E4=00=00=00=00=8B= +U=F0=8B=02=85=C0th=C7E=D8=00=00=00=00=8BM=EC=89=C3=89M=DC=8B}=E4=85=FFt=17= +=FC=8Bu=D8=8B}=EC=8BE=E8=01=F7=8Bu=E09=C0=89=C1=F3=A6tr=89\$=08=A1d=A6=05= +=08=BBq=8A=05=08=89\$=04=89=04$=E8@y=FF=FF=8BU=DC=89U=E0=FFE=E4=8BM=E8=01= +M=D8=8BU=E4=01M=DC=8BM=F0=8B=04=91=85=C0=89=C3u=A7=A1d=A6=05=08=8BP=14;P=18= +s=0E=C6=02=0A=FF@=14=83=C4,[^_]=C3=C7E=0C=0A=00=00=00=89E=08=83=C4,[^_]=E9= +~x=FF=FF=8D=B6=00=00=00=00=89\$=08=A1d=A6=05=08=B9{=8A=05=08=89L$=04=89=04= +$=E8=CEx=FF=FF=EB=92=8Dt&=00U=89=E5=83=EC(=89]=F4=8BE=10=8B]=0C=89u=F8=8B= +u=18=89}=FC=8B}=14=89t$=0C=89D$=04=89|$=08=89=1C$=E8!=FD=FF=FF=85=C0=89=C2= +x=0F=8B]=F4=89=D0=8Bu=F8=8B}=FC=89=EC]=C3=89D$=08=8BE=08=89\$=04=89=04$=E8= +=E9=FD=FF=FF=89t$=08=8BE=10=89|$=04=89=04$=E8v=FE=FF=FF=FFU=1C=BA=FF=FF=FF= +=FF=EB=C1=8D=B6=00=00=00=00=8D=BF=00=00=00=00U=89=E5WVS=83=EC=08=8BE=0C=C7= +E=F0=00=00=00=00=8B]=14=8B=00=85=C0t:=FC=C7E=EC=00=00=00=00=8D=B6=00=00=00= +=00=8D=BF=00=00=00=00=8B}=10=89=D9=8BE=EC=8Bu=08=01=C79=DB=F3=A6t=1D=FFE=F0= +=8BU=0C=01]=EC=8BE=F0=8B=04=82=85=C0u=DA1=C0=83=C4=08[^_]=C3=8BU=F0=8BM=0C= +=8B=04=91=EB=ED=90=90=90=90=90U=89=E5=8BM=08=0F=B6=01=89=CA=84=C0t=0D=90<= +/t=0CB=0F=B6=02=84=C0u=F4]=89=C8=C3B=0F=B6=02=FF=FF=FF=90=90=90=90=90=90= +=90=90=90=90=90=90=90=90U=89=E5W1=FFVS=83=EC=0C=8B]=10=8Bu=0C=85=DBu=0B=83= +=C4=0C=89=F8[^_]=C3=90=89\$=08=8BE=08=89t$=04=89=04$=E8}=1F=00=00=83=F8=FF= +t=DD=85=C0t=0A=01=C7=01=C6)=C3u=DC=EB=CF=E8=1Du=FF=FF=C7=00=1C=00=00=00=EB= +=C2=90=90=90=90=90=90=90=90=90=90=90=90=90U1=C0=89=E5=83=EC=18=89D$=04=8B= +E=08=89]=F4=BB=01=00=00=00=89u=F8=89}=FC=89=04$=E8=B3t=FF=FF=85=C0t=1E=80= +8Cu=06=80x=01=00t=11=FC=BF=95=8A=05=08=B9=06=00=00=00=89=C6=F3=A6u=021=DB= +=89=D8=8Bu=F8=8B]=F4=8B}=FC=89=EC]=C3=90=90=90=90=90=90=90=90=90=90U=89=E5= +=83=ECH=89]=F4=8B]=08=89u=F8=89}=FC=83=FB=01=DBm=0Ctf=DB-=C0=8A=05=08=DD=E9= +=DF=E0=9EvY=D9=C0=DB<$1=F61=FF=DB}=D8=E8=880=00=00=89E=D0=85=DB=89U=D4=DB= +m=D8u[=DFm=D0=8BE=D4=85=C0xF=DA=E9=DF=E0=9Ez=02t=08=BE=01=00=00=001=FF=90= +=8BE=D0=8BU=D4=01=F0=11=FARP=DF,$=83=C4=08=85=D2x=17=8Dv=00=8D=BC'=00=00=00= +=00=8B]=F4=8Bu=F8=8B}=FC=89=EC]=C3=D8=05=E0=8A=05=08=EB=EB=D8=05=E0=8A=05= +=08=EB=B2=8Dv=00=DD=D8=EB=BC=8D=B6=00=00=00=00=8D=BF=00=00=00=00U=89=E5W=BF= +=FF=FF=FF=FFVS=83=EC\=8BE=14=8B]=08=89=04$=E8=ACt=FF=FF=89E=B4=8Bu=0C=8DU= +=B8=89\$=04=89=14$=01=F3=89t$=08=8D=B4&=00=00=00=00=8D=BC'=00=00=00=00=E8= +=83u=FF=FF=8BU=10=0F=B6=02=84=C0t=0C<~=89=F7w=03=0F=B6=F8=FFE=109=FEs=02=89= +=F7=89|$=08)=FE=8DE=B8=01=F0)=FB=89D$=04=89=1C$=E8Nu=FF=FF=85=F6t=18=8BU=B4= +=8BE=B4)=D3=89D$=08=8BU=14=89=1C$=89T$=04=EB=AA=83=C4\=89=D8[^_]=C3U=89=E5= +WVS=81=EC=AC=00=00=00=8BM$=C7E=C0=FF=FF=FF=FF=8BE=08=8BU=0C=89M=DC=8Bu=18= +=8BM=14=89E=E0=8B}=1C1=C0=89U=E4=83=E1=03=8BU =F6E=14 =89M=D4=89U=D8=0F=95= +=C0=C7E=B0b\=05=08=C7E=AC=01=00=00=00=C7E=A4E\=05=08H=83=E0=E8=05=00=04=00= +=00=89E=D0=E8=0Fs=FF=FF=89=C3=8B=00=89E=A0=89=04$=E8=B0s=FF=FF=89=C2=8D@=FF= +=83=F8=0Fw=09=89U=AC=8BM=A0=89M=B0=8BC=08=8B[=04=89E=A8=89=1C$=E8=8Cs=FF=FF= +=83=F8=10w=03=89]=A4=8BU=10=81=C2=87=02=00=009}=DC=89U=B8=89U=BC=0F=87=89= +=06=00=00r=099u=D8=0F=87~=06=00=00=894$=8BE=D8=8BU=DC=89|$=04=89D$=08=89T= +$=0C=E8=1F4=00=00=89=D1=09=C1uy=894$=8BE=D8=8BU=DC=89|$=04=89D$=08=89T$=0C= +=E8=CF2=00=00=89D$=08=8B]=E0=8BE=E0=89T$=0C=F7d$=08=89=D1=89E=C8=8BD$=0C=8B= +U=C8=0F=AF=D8=8BD$=08=89=14$=01=D9=8B]=E4=0F=AF=D8=01=D9=89M=CC=8BM=CC=89= +L$=04=E8=8E2=00=00=8BM=E41=D1=8BU=E01=D0=09=C1=0F=84=0C=03=00=00=8D=B6=00= +=00=00=00=8D=BF=00=00=00=00=DFm=D8=8BE=DC=85=C0=0F=88=E7=02=00=00=DFm=E0=8B= +E=E4=85=C0=0F=88=CE=02=00=00WV=DF,$=83=C4=08=85=FF=0F=88=B3=02=00=00=F6E=14= +=10=DE=F2=DE=C9=D9=C0=DB=BDx=FF=FF=FF=0F=84i=02=00=00=DD=D8=C7E=C0=00=00=00= +=00=8BE=D01=D2R=D9=E8P=DF,$=83=C4=08=8D=B6=00=00=00=00=DB=ADx=FF=FF=FF=D9= +=CA=D8=C9=FFE=C0=D9=C0=D8=CA=D9=CB=DD=EB=DF=E0=DD=DA=9Er=06=83}=C0=08|=DE= +=DD=D8=DB=ADx=FF=FF=FF=BE=E4=8A=05=08=8BE=D4=DE=F1=89=04$=D9=C0=DB=BDx=FF= +=FF=FF=DB|$=04=E8=A8=FC=FF=FF=89t$=04=8BU=10=DB|$=08=89=14$=E8=0Du=FF=FF=8B= +M=10=89=0C$=E8=02r=FF=FF=89=C3=89=C2=8Bu=AC=8BE=ACF=83=C0=02=F6E=14 u=06=8B= +E=AC=83=C0=039=D8=0F=82r=01=00=00=F6E=14=08t=12=8BE=10=80|=02=FF0=0F=84^=01= +=00=00=8Dt&=00=89\$=08=8BM=B8=8BE=10)=D9=89M=BC=89=0C$=89D$=04=E8Mq=FF=FF= +=8BM=BC=01=CB)=F3=F6E=14=04=0F=85=04=01=00=00=F6E=14@t^=8BE=C0=85=C0xk=8B= +]=C0=85=DBt =8BE=B8=FFE=B8=F6E=14 u=08=83}=C0=01=B2kt=0A=8BM=C0=0F=B6=91=9C= +=8A=05=08=88=10=80}=14=00y*=F6E=14 t=11=8BM=C0=85=C9t=0A=8BE=B8=C6=00i@=89= +E=B8=8BU=B8=C6=02BB=89U=B8=89=F6=8D=BC'=00=00=00=00=8BM=B8=C6=01=00=8BE=BC= +=81=C4=AC=00=00=00[^_]=C3=C7E=C0=00=00=00=001=F6=8BM=DC=89=B5l=FF=FF=FF=BF= +=01=00=00=009=8Dl=FF=FF=FF=89=BDh=FF=FF=FFsV=8Bu=D01=FF=89=F6=FFE=C0=83}=C0= +=08=0F=84\=FF=FF=FF=8B=85h=FF=FF=FF=F7=E6=89=C1=8B=85h=FF=FF=FF=89=8Dh=FF= +=FF=FF=0F=AF=C7=01=C2=8B=85l=FF=FF=FF=0F=AF=C6=8D=1C=10;]=DC=89=9Dl=FF=FF= +=FFr=C1=0F=87$=FF=FF=FF;M=D8r=B6=E9=1A=FF=FF=FF=8BE=D89=85h=FF=FF=FFr=9F=E9= +=0A=FF=FF=FF=90=8BE=A8=8BM=A4=8BU=BC=89D$=08=8BE=BC=89L$=0C=89=14$)=C3=89= +\$=04=E8=AE=FB=FF=FF=89E=BC=E9=D2=FE=FF=FF=DB-=D0=8A=05=08=BB=EA=8A=05=08= +=8BE=D4=DB=ADx=FF=FF=FF=89=04$=DE=C9=DB|$=04=E8=D4=FA=FF=FF=DB-=D0=8A=05=08= +=89\$=04=8BE=10=DE=F9=89=04$=DB|$=08=E81s=FF=FF=8BU=10=89=14$=E8&p=FF=FF=89= +=C31=F6=E9U=FE=FF=FF=DB|$=04=8BU=D4=BF=EA=8A=05=08=89=14$=E8=91=FA=FF=FF=89= +|$=04=8BM=10=DB|$=08=89=0C$=E8=F6r=FF=FF=8BE=10=89=04$=E8=EBo=FF=FF=EB=C3= +=D8=05=E0=8A=05=08=E9B=FD=FF=FF=D8=05=E0=8A=05=08=E9'=FD=FF=FF=D8=05=E0=8A= +=05=08=E9=0E=FD=FF=FF=C7E=C4=00=00=00=00=C7E=B4=00=00=00=00=F6E=14=10=0F=84= +[=01=00=00=C7E=C0=00=00=00=001=D2=8BE=D0;U=CC=0F=87F=01=00=00r=09;E=C8=0F= +=87;=01=00=00=89=C6=89=D7=89t$=08=8BM=CC=8BU=C8=89|$=0C=89L$=04=89=14$=E8= +J0=00=00=89t$=08=89=C1=8BU=C4=89|$=0C=C1=E1=02=01=C1=8D=04J1=D2=F7u=D0=89= +=85t=FF=FF=FF=8BE=B4=D1=F8=8D=1CP=8BE=C8=8BU=CC=89=04$=89T$=04=E8=E0.=00=00= +;]=D0=89U=CC=8B=95t=FF=FF=FF=89E=C8=89U=C4=0F=83(=02=00=00=8BM=B4=8D=04=19= +=85=C0=0F=95=C0=0F=B6=C0=FFE=C01=D2;U=CC=89E=B4=8BE=D0w=11r=05;E=C8w=0A=83= +}=C0=08=0F=8Ce=FF=FF=FF=83}=CC=00=0F=87=92=00=00=00=83}=C8=09=0F=87=88=00= +=00=00=83}=D4=01=0F=84=BD=01=00=00=8BE=D4=85=C0u!=8B}=B4=85=FF~=1A=C7E=B4= +=00=00=00=00=8B=8Dt=FF=FF=FFA=89M=C4=83=F9=0A=0F=84=81=01=00=00=83}=CC=00= +wP=83}=C8=09wJ=8Bu=C4=85=F6u=06=F6E=14=08u=3D=8BE=B8=8BU=B8H=89E=BC=0F=B6= +E=C4=040=88B=FF=8BM=AC=8BE=B0)M=BC=89L$=08=89D$=04=8BU=BC=89=14$=E8co=FF=FF= +=C7E=B4=00=00=00=00=C7E=C4=00=00=00=00=83}=D4=01=0F=84=EB=00=00=00=8B]=D4= +=85=DBuJ=8BE=C4=8BM=B4=01=C8=85=C0~>=83E=C8=01=83U=CC=00=F6E=14=10t0=8BM=CC= +1=D2=8BE=D01=D1=8BU=C81=D0=09=C1u=1D=83}=C0=08}=17=FFE=C0=F6E=14=08t=7F=C7= +E=C8=01=00=00=00=C7E=CC=00=00=00=00=8B]=BC=EB=0D=90=90=90=90=90=90=90=90=90= +=90=90=90=90=8BM=CC=B8=0A=00=00=00=8BU=C8=89D$=081=C0=89L$=04=89D$=0C=89=14= +$=E8=9F.=00=00=FFM=BC=040=8BM=BC=88=01=B8=0A=00=00=00=8BU=CC=89D$=081=C0=89= +D$=0C=8BE=C8=89T$=04=89=04$=E8D-=00=00=89U=CC=8BU=CC=89E=C8=0BU=C8u=A6=E9= +=E3=FB=FF=FF=FFM=BC=8BU=BC=C6=020=8BM=AC=8BE=B0=89L$=08)=CA=89U=BC=89D$=04= +=89=14$=E8en=FF=FF=E9X=FF=FF=FF=8BE=B4=99=89=C1=8BE=C8=89=D31=D2=83=E0=01= +=01=C1=11=D3=83=FB=00w=05=83=F9=00v=12=8BE=C4@=83=F8=05=0F=8F=FD=FE=FF=FF= +=E96=FF=FF=FF=83}=C4=05=EB=EF=83E=C8=01=C7E=C4=00=00=00=00=83U=CC=00=E9k=FE= +=FF=FF=8B=85t=FF=FF=FF=8BU=B4=83=E0=01=01=D0=83=F8=02=0F=8F:=FE=FF=FF=E9O= +=FE=FF=FF=89=F6=8BU=B4=8D=04=1A9E=D0=19=C0=F7=D0=83=C0=03=E9=D1=FD=FF=FF=89= +=F9=09=F1=0F=84=11=FA=FF=FF=89t$=08=8BE=D8=8BU=DC=89|$=0C=89=04$=89T$=04=E8= +=97-=00=00=89=D1=09=C1=0F=85=ED=F9=FF=FF=89t$=08=8BE=D8=8BU=DC=89|$=0C=89= +=04$=89T$=04=E8C,=00=00=89U=9C=8BM=E4=89T$=0C=8BU=E0=89L$=04=89E=98=89D$=08= +=89=14$=E8S-=00=00=89=C3=89=D6=C1=E3=02=0F=A4=C6=02=01=C3=8BE=98=11=D6=8B= +U=9C=0F=A4=DE=01=01=DB=89D$=08=89T$=0C=89=1C$=89t$=04=E8$-=00=00=89=D1=89= +=C2=01=D2=89U=90=8BU=9C=0F=A4=C1=01=8BE=98=89M=94=8BM=E4=89T$=0C=8BU=E0=89= +L$=04=89D$=08=89=14$=E8=C4+=00=00=89E=C8=8BE=98=89U=CC=8BU=9C=89D$=08=89=1C= +$=89T$=0C=89t$=04=E8=A4+=00=00=89E=C4=8BU=9C9U=94w*r=08=8BE=989E=90s =83}= +=94=00=C7E=B4=01=00=00=00=0F=87=1C=FC=FF=FF=83}=90=00=0F=87=12=FC=FF=FF=E9= +=06=FC=FF=FF=8BU=949U=9Cw=16r=08=8BE=909E=98s=0C=C7E=B4=03=00=00=00=E9=EF= +=FB=FF=FF=C7E=B4=02=00=00=00=E9=E3=FB=FF=FF=90=8Dt&=00U=89=E5=83=EC=08=C7= +=04$=AA\=05=08=E8Fj=FF=FF1=C9=BA=00=02=00=00=85=C0u=07=BA=00=04=00=001=C9= +=89=EC=89=D0=89=CA]=C3=8Dt&=00U=89=E5=83=EC(=89]=F4=8B]=08=89u=F81=F6=85=DB= +=89}=FC=8B}=0C=0F=84=EC=00=00=00=80;'=0F=84=D8=00=00=00=89=1C$=B8=04=00=00= +=00=B9=A8=8A=05=08=89D$=0C=B8=B4=8A=05=08=89D$=08=89L$=04=E8U=EE=FF=FF=85= +=C0x1=C7=07=01=00=00=00=8B=14=85=B4=8A=05=08=C7G=04=00=00=00=00=09=D6=8BE= +=10=8901=C0=8B]=F4=8Bu=F8=8B}=FC=89=EC]=C3=8D=B4&=00=00=00=00=89|$=0C=B8=F0= +=8A=05=08=89D$=101=C0=89D$=08=8DE=F0=89D$=04=89=1C$=E8=AE=1B=00=00=85=C0u= +=C6=8BU=F0=80:=00uB=0F=B6=03,0<=09v=AE=89=F6=8D=BC'=00=00=00=009=D3t=0CC=0F= +=B6=03,0<=09w=F2=EB=95=83=CE@=80{=FFBt=05=83=CE =EB=87=81=CE=80=00=00=00=80= +{=FEi=0F=85w=FF=FF=FF=EB=E9=B8=02=00=00=00=E9r=FF=FF=FF=8D=B6=00=00=00=00= +=BE=04=00=00=00C=E9=1D=FF=FF=FF=C7=04$=97\=05=08=E8=11i=FF=FF=85=C0=89=C3= +=0F=85=FE=FE=FF=FF=E8=AA=FE=FF=FF=89=07=89W=04=E95=FF=FF=FFU=89=E5=83=EC(= +=0F=B6E=0C=89]=F4=8B]=10=89u=F8=8Bu=08=88E=EF=8DE=F0=89}=FC=89D$=08=89\$=04= +=894$=E8=A1=FE=FF=FF=8BS=04=89=C1=8B=03=89=D7=09=C7=0F=84=E5=00=00=00=85=C9= +t,=80}=EF=00t&=83=F9=01=0F=84=A1=00=00=00=83=F9=01=0F=82=DD=00=00=00=83=F9= +=02tb=83=F9=03t=1A=8Dv=00=8D=BC'=00=00=00=00=8BE=F0=8B]=F4=8Bu=F8=8B}=FC=89= +=EC]=C3=C7=04$=00=00=00=00=B8=05=00=00=00=89D$=08=B8=02=8B=05=08=89D$=04=E8= +jh=FF=FF=89D$=0C=B8=0D=8B=05=08=89t$=10=89D$=08=C7=04$=02=00=00=001=C0=89= +D$=04=E8=87l=FF=FF=EB=AD=C7=04$=00=00=00=00=B9=02=8B=05=08=BB=05=00=00=00= +=89\$=08=89L$=04=E8'h=FF=FF=89D$=0C=BA@=8B=05=08=89t$=10=89T$=08=EB=BB=C7= +=04$=00=00=00=00=B8=05=00=00=00=BF=02=8B=05=08=89D$=08=89|$=04=E8=F6g=FF=FF= +=89t$=10=BE=1F=8B=05=08=89D$=0C=89t$=08=EB=8A=E8v=FD=FF=FF=89=03=B9=01=00= +=00=00=89S=04=E9=07=FF=FF=FF=E8=AAh=FF=FF=90=90U=89=E5WVS=83=EC=0C=8B=1D=A8= +=A8=05=08=8Bu=08=85=DBt=15=8Dv=00=8D=BC'=00=00=00=0093tN=8B[=08=85=DBu=F5= +=894$=E8=E5g=FF=FF=C7=04$=0C=00=00=00=89=C7=E8=8F=14=00=00=8901=D2=89=C3=85= +=FFt=0C=8B=07=89=04$=E8{=15=00=00=89=C2=89S=04=A1=A8=A8=05=08=89C=08=89=D0= +=89=1D=A8=A8=05=08=83=C4=0C[^_]=C3=8BC=04=EB=F3=89=F6=8D=BC'=00=00=00=00U= +=89=E5WVS=83=EC=0C=8B=1D=A8=A8=05=08=8B}=08=85=DBt =0F=B6=07=88E=F3=8Dt&=00= +=0F=B6U=F3=8BC=048=10=0F=84=A5=00=00=00=8B[=08=85=DBu=EA=8B=1D=AC=A8=05=08= +=85=DBt"=0F=B6=07=88E=F3=8Dv=00=8D=BC'=00=00=00=00=0F=B6U=F3=8BC=048=10te= +=8B[=08=85=DBu=EE=89<$=E8=FEi=FF=FF=C7=04$=0C=00=00=00=89=C6=E8=D8=13=00=00= +=89<$=89=C3=E8=CE=14=00=00=89C=04=85=F6t=1F=8BF=08=89=DA=89=03=A1=A8=A8=05= +=08=89C=08=89=1D=A8=A8=05=08=83=C4=0C=89=D0[^_]=C3=A1=AC=A8=05=081=D2=89C= +=08=89=1D=AC=A8=05=08=EB=E4=8D=B6=00=00=00=00=89|$=04=89=04$=E8Lf=FF=FF1=D2= +=85=C0u=89=EB=CA=89|$=04=89=04$=E88f=FF=FF=85=C0=89=DA=0F=85E=FF=FF=FF=EB= +=B2=8Dt&=00U=89=E5WVS=83=EC=0C=8B=1D=B0=A8=05=08=8Bu=08=85=DBt=15=8Dv=00=8D= +=BC'=00=00=00=0093tN=8B[=08=85=DBu=F5=894$=E8=95f=FF=FF=C7=04$=0C=00=00=00= +=89=C7=E8=1F=13=00=00=8901=D2=89=C3=85=FFt=0C=8B=07=89=04$=E8=0B=14=00=00= +=89=C2=89S=04=A1=B0=A8=05=08=89C=08=89=D0=89=1D=B0=A8=05=08=83=C4=0C[^_]=C3= +=8BC=04=EB=F3=89=F6=8D=BC'=00=00=00=00U=89=E5WVS=83=EC=0C=8B=1D=B0=A8=05=08= +=8B}=08=85=DBt =0F=B6=07=88E=F3=8Dt&=00=0F=B6U=F3=8BC=048=10=0F=84=A5=00=00= +=00=8B[=08=85=DBu=EA=8B=1D=B4=A8=05=08=85=DBt"=0F=B6=07=88E=F3=8Dv=00=8D=BC= +'=00=00=00=00=0F=B6U=F3=8BC=048=10te=8B[=08=85=DBu=EE=89<$=E8~d=FF=FF=C7=04= +$=0C=00=00=00=89=C6=E8h=12=00=00=89<$=89=C3=E8^=13=00=00=89C=04=85=F6t=1F= +=8BF=08=89=DA=89=03=A1=B0=A8=05=08=89C=08=89=1D=B0=A8=05=08=83=C4=0C=89=D0= +[^_]=C3=A1=B4=A8=05=081=D2=89C=08=89=1D=B4=A8=05=08=EB=E4=8D=B6=00=00=00=00= +=89|$=04=89=04$=E8=DCd=FF=FF1=D2=85=C0u=89=EB=CA=89|$=04=89=04$=E8=C8d=FF= +=FF=85=C0=89=DA=0F=85E=FF=FF=FF=EB=B2=90=90=90=90U=89=E5WVS=83=EC=1C=8Bu=0C= +=8BE=10=8B]=08=85=F6=C6@=15=00=8Dx=15xS=8Dv=00=89=1C$1=C9=B8=0A=00=00=00=89= +L$=0CO=89t$=04=89D$=08=E8t#=00=00=040=BA=0A=00=00=00=88=071=C0=89=1C$=89t= +$=04=89T$=08=89D$=0C=E8=C5!=00=00=89=D1=89=C3=09=C1=89=D6u=BB=83=C4=1C=89= +=F8[^_]=C3=90=89=1C$=B8=0A=00=00=00O=89D$=081=C0=89t$=04=89D$=0C=E8$#=00=00= +=B10(=C1=B8=0A=00=00=00=88=0F=89D$=081=C0=89=1C$=89t$=04=89D$=0C=E8s!=00=00= +=89=C3=89=D6=89=D0=09=D8u=B9O=C6=07-=EB=A8=90=90=90U=89=E5=83=EC=18=89]=FC= +=8B]=08=89=1C$=E8De=FF=FF=89D$=04=8BU=0C=89=1C$=89T$=08=E8=09=00=00=00=8B= +]=FC=89=EC]=C3=89=F6U=89=E5W1=FFVS=83=EC,=8Bu=08=8BE=0C=01=F0=89E=E0=E8=DD= +f=FF=FF=83=F8=01=0F=86'=01=00=00;u=E0sG=8D=B4&=00=00=00=00=0F=BE=06=83=F8= +_=0F=8F=04=01=00=00=83=F8A}!=83=F8 |:=83=F8#~=17=83=E8%=83=F8=1A=EB=0D=90= +=90=90=90=90=90=90=90=90=90=90=90=90w=1EFG;u=E0r=C7=8D=B4&=00=00=00=00=89= +=F8=83=C4,[^_]=C3=8D=B6=00=00=00=00=C7E=E8=00=00=00=00=C7E=EC=00=00=00=00= +=89=F6=89t$=04=8DE=E8=89D$=0C=8BE=E0)=F0=89D$=08=8DE=E4=89=04$=E8=D9d=FF=FF= +=83=F8=FF=89=C3tr=83=F8=FEtV=85=C0u=05=BB=01=00=00=00=8BE=E4=89=04$=E89g=FF= +=FF=85=C0x=1D=01=C7=8DE=E8=01=DE=89=04$=E8=E6f=FF=FF=85=C0t=AA=E9y=FF=FF=FF= +=90=8Dt&=00=F6E=10=02=B8=FF=FF=FF=FF=0F=85s=FF=FF=FF=8BE=E4=89=04$=E8=9Ed= +=FF=FF=85=C0u=C7G=EB=C4=F6E=10=01=B8=FF=FF=FF=FF=0F=85R=FF=FF=FF=8Bu=E0=E9= +;=FF=FF=FF=F6E=10=01=B8=FF=FF=FF=FF=0F=84+=FF=FF=FF=E96=FF=FF=FF=8Dt&=00=83= +=E8a=83=F8=1D=E9=15=FF=FF=FF;u=E0=0F=83=1C=FF=FF=FF=E8=EFf=FF=FF=89=C1=90= +=8Dt&=00=0F=B6=06F=0F=B6=D0=8B=01=0F=B7=14P=F6=C6@t=0EG;u=E0r=E8=E9=F3=FE= +=FF=FF=8Dv=00=F6E=10=02=B8=FF=FF=FF=FF=0F=85=E3=FE=FF=FF=D1=EA=83=F2=01=83= +=E2=01=01=D7=EB=D8=90=90=90=90=90U=89=E5WVS=83=EC=1C=8B]=08=85=DB=0F=84=9C= +=00=00=00=8BE=0C=89=04$=E8=81=EA=FF=FF=89E=F0=89=1C$=E8^c=FF=FF=8BU=F0=89= +=C6=8DD=02=02=89=04$=E8=CDb=FF=FF=89=C71=C0=85=FFtX=89\$=04=89t$=08=89<$=E8= +=C5e=FF=FF=85=F6=89=C3t=17=80x=FF/tD=8BU=0C=80:/t=09=C6=00/C=90=8Dt&=00=8B= +E=10=85=C0t=05=8BE=10=89=18=89=1C$=8BU=F0=8BE=0C=89T$=08=89D$=04=E8=F6c=FF= +=FF=8BU=F0=C6=04=1A=00=89=F8=83=C4=1C[^_]=C3=8BE=0C=808/u=C5K=EB=C2=89=F6= +=8BE=0C=89=04$=E8=0Dd=FF=FF=8BU=10=89=C7=85=D2t=D5=8BU=10=89=02=EB=CE=90=8D= +t&=00U=89=E5=83=EC=18=8BE=10=89D$=08=8BE=0C=89D$=04=8BE=08=89=04$=E8=11=FF= +=FF=FF=85=C0t=04=89=EC]=C3=E8=F4=0D=00=00=90=90=90=90U=89=E5=83=EC=18=8BE= +=0C=89D$=08=B8=05=00=00=00=89D$=04=8BE=08=89=04$=E8O=0A=00=00=89=EC]=C3=8D= +t&=00=8D=BC'=00=00=00=00U=89=E5=83=EC=08=C7=04$=00=00=00=00=8BE=08=89D$=04= +=E8=B7=FF=FF=FF=89=EC]=C3=90=90=90U=89=E5=83=EC=18=89]=F8=89u=FC=E8Wa=FF=FF= +=8B0=89=C3=C7=04$$=00=00=00=E8=EF=0D=00=00=8BU=08=89=C1=85=D2tF=8B=02=89=01= +=8BB=04=89A=04=8BB=08=89A=08=8BB=0C=89A=0C=8BB=10=89A=10=8BB=14=89A=14=8B= +B=18=89A=18=8BB=1C=89A=1C=8BB =89A =89=C8=893=8B]=F8=8Bu=FC=89=EC]=C3=8Dt= +&=00=BA=C0=A9=05=08=EB=B3=89=F6=8D=BC'=00=00=00=00U=89=E5=8BE=08=85=C0t=06= +]=8B=00=C3=89=F6=B8=C0=A9=05=08=EB=F3=89=F6=8D=BC'=00=00=00=00U=89=E5=8BU= +=08=85=D2t=07=8BE=0C=89=02]=C3=BA=C0=A9=05=08=EB=F2=90=8D=B4&=00=00=00=00= +U=89=E5=83=EC=08=0F=B6M=0C=89=1C$=8BE=08=89t$=04=8B]=10=88=CA=C0=EA=05=0F= +=B6=D2=C1=E2=02=85=C0=8Dt=02=04t&=8B=16=83=E1=1F=83=E3=01=89=D0=D3=F8=83=E0= +=011=C3=D3=E31=DA=89=16=8B=1C$=8Bt$=04=89=EC]=C3=8Dt&=00=8D=B2=C4=A9=05=08= +=EB=D2=90=8D=B4&=00=00=00=00U=B8=05=00=00=00=89=E5=83=EC=18=89]=FC=8B]=08= +=89D$=08=C7=04$=00=00=00=00=89\$=04=E8=C3_=FF=FF9=D8t=07=8B]=FC=89=EC]=C3= +=83}=0C=06u=F3=B8=82=8B=05=08=EB=EC=8Dv=00U=89=E5WVS=83=EC|=C7E=D4=00=00=00= +=00=C7E=D0=00=00=00=00=C7E=CC=00=00=00=00=C7E=C8=00=00=00=00=E8~b=FF=FFH=0F= +=94=C0=83}=18=06=0F=B6=C0=89E=C4w4=8BE=18=FF$=85=DC=8B=05=08=8BU=0C9U=D4s= +=06=8BM=08=C6=01"=C7E=D4=01=00=00=00=C7E=C8=01=00=00=00=C7E=D0=82=8B=05=08= +=C7E=CC=01=00=00=00=C7E=D8=00=00=00=00=8Dt&=00=8D=BC'=00=00=00=00=83}=14=FF= +=0F=84=06=05=00=00=8BU=149U=D8=0F=84=AB=04=00=00=8B]=C8=85=DBtC=8BM=CC=85= +=C9t<=8BE=D8=8BU=CC=01=D0;E=14w/=FC=8B]=CC=8Bu=10=8BE=D8=8B}=D0=89=D9=01=C6= +9=DB=F3=A6u=18=8Bu=0C9u=D4s=0A=8BE=08=8BU=D4=C6=04=02\=FFE=D4=8Dv=00=8BM=10= +=8B]=D8=0F=B6=0C=0B=0F=B6=C1=83=F8~=88M=A8=0F=87;=02=00=00=FF$=85=F8=8B=05= +=08=8BE=C8=8Bu=D8=85=C0=8DV=01=0F=84=9F=00=00=00=8BE=0C9E=D4s=0A=8BU=08=8B= +M=D4=C6=04=11\=FFE=D4=8B]=0C9]=D4s=0A=8Bu=08=8BE=D4=C6=0400=FFE=D4=8BU=0C= +9U=D4s=0A=8BM=08=8B]=D4=C6=04=0B0=FFE=D4=C6E=A80=8D=B6=00=00=00=00=8D=BF=00= +=00=00=00=8B]=C8=8BM=D8=85=DB=8DQ=01tC=0F=B6E=A8=8B]=1C=0F=B6M=A8=C0=E8=05= +=0F=B6=C0=8BD=83=04=83=E1=1F=D3=F8=A8=01t%=8BE=0C9E=D4s=0A=8BU=08=8BM=D4=C6= +=04=11\=FFE=D4=8B]=D8=8DS=01=8Dv=00=8D=BC'=00=00=00=00=8Bu=0C9u=D4s=0D=0F= +=B6E=A8=8BM=08=8B]=D4=88=04=0B=FFE=D4=89U=D8=E9=B0=FE=FF=FF=B0a=8D=B4&=00= +=00=00=00=8D=BC'=00=00=00=00=8B}=C8=8BM=D8=85=FF=8DQ=01t=C3=88E=A8=EB=99=B0= +b=EB=EA=B0t=83}=18=01u=E2=8BU=1C=8BM=14=8B]=10=89T$=14=8Bu=0C=BA=02=00=00= +=00=89T$=10=8BE=08=89L$=0C=89\$=08=89t$=04=89=04$=E8=D2=FD=FF=FF=83=C4|[^= +_]=C3=B0n=EB=BE=B0v=EB=A2=B0f=EB=9E=B0r=EB=B2=83}=18=01t=B2=83}=18=02=0F=85= +=0A=FF=FF=FF=8B]=0C9]=D4s=0A=8Bu=08=8BE=D4=C6=040'=FFE=D4=8BU=0C9U=D4s=0A= +=8BM=08=8B]=D4=C6=04=0B\=FFE=D4=8Bu=0C9u=D4s=0A=8BE=08=8BU=D4=C6=04=02'=FF= +E=D4=E9=C6=FE=FF=FF=83}=18=01=0F=84Z=FF=FF=FF=83}=18=03=0F=85=B2=FE=FF=FF= +=8BU=D8=83=C2=02;U=14=0F=83=A3=FE=FF=FF=8Bu=10=8BE=D8=80|0=01?=0F=85=92=FE= +=FF=FF=0F=B6L0=02=0F=BE=C1=83=E8!=83=F8=1D=0F=87~=FE=FF=FF=FF$=85=F4=8D=05= +=08=89U=D8=8BU=0C9U=D4=88M=A8s=0A=8BM=08=8B]=D4=C6=04=0B?=FFE=D4=8Bu=0C9u= +=D4s=0A=8BE=08=8BU=D4=C6=04=02\=FFE=D4=8BM=0C9M=D4=0F=83m=FF=FF=FF=8B]=08= +=8Bu=D4=C6=04=1E?=E9^=FF=FF=FF=8B]=C4=85=DB=0F=84=F7=00=00=00=C7E=A4=01=00= +=00=00=E8=AD`=FF=FF=0F=B6U=A8=8B=00=0F=B7=14P=81=E2=00@=00=00=89U=A0=8BE=C8= +=8B]=D8=85=C0=8DS=01=0F=84A=FE=FF=FF=8BE=A0=85=C0=0F=85=E6=FD=FF=FF=8BM=D8= +=8BE=A4=01=C1=8D=B4&=00=00=00=00=8D=BC'=00=00=00=00=8B}=C8=85=FFti=8Bu=A0= +=85=F6ub=8Bu=0C9u=D4s=0A=8BE=08=8BU=D4=C6=04=02\=FFE=D4=8B]=0C9]=D4s=12=0F= +=B6E=A8=8Bu=08=8BU=D4=C0=E8=06=040=88=042=FFE=D4=8B]=0C9]=D4s=14=0F=B6E=A8= +=8Bu=08=8BU=D4=C0=E8=03$=07=040=88=042=80e=A8=07=FFE=D4=80E=A80=89=F6=8D=BC= +'=00=00=00=00=8BU=D8B9=D1=0F=86=A4=FD=FF=FF=8B]=0C9]=D4s=0D=0F=B6E=A8=8B]= +=08=8Bu=D4=88=04=1E=FFE=D4=8BE=10=89U=D8=0F=B6=04=02=88E=A8=E9Z=FF=FF=FF=83= +}=14=FF=C7E=E0=00=00=00=00=C7E=E4=00=00=00=00=C7E=A4=00=00=00=00=C7E=A0=01= +=00=00=00=0F=84=AD=00=00=00=8Dt&=00=8D]=E0=8BM=A4=8BE=14=89\$=0C=8Bu=10=8B= +]=D8=01=CB)=D8=01=DE=89D$=08=8DE=DC=89t$=04=89=04$=E8=AC\=FF=FF=85=C0=89=C7= +t3=83=F8=FFti=83=F8=FEt8=8BE=DC=89=04$=E8Q^=FF=FF=85=C0=8DM=E0=0F=94=C0=01= +}=A4=89=0C$=0F=B6=C0H!E=A0=E8=B7^=FF=FF=85=C0t=9B=83}=A4=01=0F=86=8F=FE=FF= +=FF=E9=A6=FE=FF=FF;]=14=C7E=A0=00=00=00=00s=E5=80>=00t=E0=FFE=A4=8BE=D8=8B= +U=A4=01=D0;E=14s=D0=8BU=10=80<=10=00u=E7=EB=C5=C7E=A0=00=00=00=00=EB=BC=8B= +M=10=89=0C$=E8=C4[=FF=FF=89E=14=E9D=FF=FF=FF=0F=B6E=A8=E9=D3=FC=FF=FF=83}= +=18=01=0F=851=FC=FF=FF=E9=CA=FC=FF=FF=8Bu=D8=85=F6=0F=85!=FC=FF=FF=EB=E4=8B= +M=D0=85=C9t+=8BM=D0=0F=B6=01=84=C0t!=8B]=0C9]=D4s=09=8Bu=08=8BU=D4=88=042= +=FFE=D0=FFE=D4=8BM=D0=0F=B6=01=84=C0u=DF=8B]=0C9]=D4s=0A=8Bu=08=8BE=D4=C6= +=040=00=8BE=D4=E9=A1=FC=FF=FF=8Dv=00=8Bu=10=8BE=D8=80<0=00=E9=F1=FA=FF=FF= +=8BU=0C9U=D4s=06=8BM=08=C6=01'=C7E=D4=01=00=00=00=C7E=D0-=8B=05=08=E9=A7=FA= +=FF=FF=C7E=C8=01=00=00=00=E9=A2=FA=FF=FF=C7=04$=84=8B=05=08=8B]=18=89\$=04= +=E8=E1=F9=FF=FF=89\$=04=89=C7=C7=04$-=8B=05=08=E8=CF=F9=FF=FF=89}=D0=89=C2= +=80?=00t%=8Bu=0C9u=D4s=0F=8BM=D0=8B]=08=8Bu=D4=0F=B6=01=88=04=1E=FFE=D0=FF= +E=D4=8BE=D0=808=00u=DB=C7E=C8=01=00=00=00=89U=D0=89=14$=E8=A6Z=FF=FF=89E=CC= +=E94=FA=FF=FF=8D=B6=00=00=00=00U=89=E5=83=EC(=89}=FC=8B}=18=89]=F4=89u=F8= +=85=FFtB=E8=ADY=FF=FF=8B0=89=C3=89|$=14=8B=07=89D$=10=8BE=14=89D$=0C=8BE=10= +=89D$=08=8BE=0C=89D$=04=8BE=08=89=04$=E8w=F9=FF=FF=893=8B]=F4=8Bu=F8=8B}=FC= +=89=EC]=C3=BF=C0=A9=05=08=EB=B7=90U=89=E5WVS=83=EC=1C=E8ZY=FF=FF=8B}=08=8B= +=00=85=FF=89E=F0=0F=880=01=00=00=8BE=089=054=A3=05=08wb=89=C3C=8D4=DD=00=00= +=00=00=89=F0=C1=E8=039=C3=0F=85=13=01=00=00=8B=0D@=A3=05=08=81=F98=A3=05=08= +=0F=84=D4=00=00=00=89t$=041=F6=89=0C$=E8=D0=05=00=00=A3@=A3=05=08=8B=154=A3= +=05=08=89t$=04=8D=0C=D0=89=D8=89=0C$)=D0=C1=E0=03=89D$=08=E8#\=FF=FF=89=1D= +4=A3=05=08=A1@=A3=05=08=8BU=08=8B<=D0=8Bt=D0=04=8BE=14=8BU=10=89|$=04=89D= +$=10=8BE=0C=89T$=0C=894$=89D$=08=E8=E5=FE=FF=FF9=C7wS=8Dx=01=8B=1D@=A3=05= +=081=C0=81=FE=C0=A8=05=08=8BU=08=0F=94=C0=89<=D3=89|$=04H!=F0=89=04$=E8H=05= +=00=00=89=C6=8BU=14=8BE=08=89t=C3=04=8BE=10=89T$=10=8BU=0C=89D$=0C=89|$=04= +=89T$=08=894$=E8=8E=FE=FF=FF=E8QX=FF=FF=8BU=F0=89=10=83=C4=1C=89=F0[^_]=C3= +=C7=04$=08=00=00=00=E8=DE=04=00=00=A3@=A3=05=08=89=C1=8B=15<=A3=05=08=A18= +=A3=05=08=89Q=04=89=01=E9=04=FF=FF=FF=E8=95X=FF=FF=E8H=04=00=00=90=8D=B4&= +=00=00=00=00U=B8=C0=A9=05=08=89=E5=83=EC=18=89D$=0C=B8=FF=FF=FF=FF=89D$=08= +=8BE=0C=89D$=04=8BE=08=89=04$=E8v=FE=FF=FF=89=EC]=C3=89=F6U=89=E5=83=EC=08= +=C7=04$=00=00=00=00=8BE=08=89D$=04=E8=B7=FF=FF=FF=89=EC]=C3=8Dv=00U=B9=08= +=00=00=00=89=E5=FC=83=EC8=8BU=0C=89}=FC=8D}=CC=89U=C81=D2=89=D0=89]=F8=8B= +]=08=F3=AB=8BU=C8=89=D8=89=13=8BU=CC=89S=04=8BU=D0=89S=08=8BU=D4=89S=0C=8B= +U=D8=89S=10=8BU=DC=89S=14=8BU=E0=89S=18=8BU=E4=89S=1C=8BU=E8=89S =8B]=F8=8B= +}=FC=89=EC]=C2=04=00=89=F6=8D=BC'=00=00=00=00U=89=E5=83=ECH=89]=FC=8BE=0C= +=8D]=C8=89=1C$=89D$=04=E8u=FF=FF=FF=83=EC=04=B8=FF=FF=FF=FF=89D$=08=8BE=10= +=89\$=0C=89D$=04=8BE=08=89=04$=E8=A3=FD=FF=FF=8B]=FC=89=EC]=C3=8D=B6=00=00= +=00=00=8D=BF=00=00=00=00U=89=E5=83=ECH=89]=FC=8BE=0C=8D]=C8=89=1C$=89D$=04= +=E8%=FF=FF=FF=8BE=14=83=EC=04=89\$=0C=89D$=08=8BE=10=89D$=04=8BE=08=89=04= +$=E8U=FD=FF=FF=8B]=FC=89=EC]=C3=8D=B4&=00=00=00=00=8D=BC'=00=00=00=00U=89= +=E5=83=EC=18=C7=04$=00=00=00=00=8BE=0C=89D$=08=8BE=08=89D$=04=E8@=FF=FF=FF= +=89=EC]=C3=8D=B6=00=00=00=00=8D=BF=00=00=00=00U=89=E5=83=ECH=0F=BEU=0C=89= +]=FC=A1=C0=A9=05=08=8D]=C8=89=1C$=89E=C8=A1=C4=A9=05=08=89T$=04=89E=CC=A1= +=C8=A9=05=08=89E=D0=A1=CC=A9=05=08=89E=D4=A1=D0=A9=05=08=89E=D8=A1=D4=A9=05= +=08=89E=DC=A1=D8=A9=05=08=89E=E0=A1=DC=A9=05=08=89E=E4=A1=E0=A9=05=08=89E= +=E8=B8=01=00=00=00=89D$=08=E8c=F5=FF=FF=89\$=0C=B8=FF=FF=FF=FF=89D$=08=8B= +E=08=C7=04$=00=00=00=00=89D$=04=E8=83=FC=FF=FF=8B]=FC=89=EC]=C3=8D=B6=00=00= +=00=00=8D=BF=00=00=00=00U=B8:=00=00=00=89=E5=83=EC=08=89D$=04=8BE=08=89=04= +$=E8F=FF=FF=FF=89=EC]=C3=90=90U=89=E5WVS=83=EC=0C=8Bu=10=8B}=0C=85=F6x:=8D= +=B6=00=00=00=00=8D=BC'=00=00=00=00=89t$=08=8BE=08=89|$=04=89=04$=E8=C5T=FF= +=FF=85=C0=89=C3y=0A=E8zU=FF=FF=838=04t=DD=83=C4=0C=89=D8[^_]=C3=BE=00=E0=FF= +=7F=EB=CC=90=90=90=90=90=90=90=90=90=90=90=90U=89=E5WVS=83=EC=1C=8BE=10=8B= +]=08=8Bu=0C=8Dx=15=C6@=15=00=8D=B4&=00=00=00=00=89=1C$1=C9=B8=0A=00=00=00= +=89L$=0CO=89t$=04=89D$=08=E8=C4=16=00=00=040=BA=0A=00=00=00=88=071=C0=89=1C= +$=89t$=04=89T$=08=89D$=0C=E8u=15=00=00=89=C3=89=D6=89=D0=09=D8u=BB=83=C4=1C= +=89=F8[^_]=C3=90U=89=E5VS=83=EC =8BU=0C=8B]=08=8BM=10=85=D2=8BE=14=8Bu=18= +=0F=84=D4=00=00=00=89D$=10=B8X=8F=05=08=89L$=0C=89T$=08=89D$=04=89=1C$=E8= +,T=FF=FF=C7=04$=00=00=00=00=B8=05=00=00=00=89D$=08=B8d=8F=05=08=89D$=04=E8= +=2ET=FF=FF=89D$=04=89t$=08=89=1C$=E8=FES=FF=FF=8BC=14;C=18sn=C6=00=0A=FFC= +=14=89\$=04=A1D=A3=05=08=89=04$=E8=BFS=FF=FF=8BC=14;C=18s5=C6=00=0A=FFC=14= +=89]=0C=BA=05=00=00=00=B8=C0=8E=05=08=89T$=08=89D$=04=C7=04$=00=00=00=00=E8= +=D0S=FF=FF=89E=08=83=C4 [^]=E9=82S=FF=FF=89=1C$=B9=0A=00=00=00=89L$=04=E8= +!S=FF=FF=EB=BE=8D=B4&=00=00=00=00=89=1C$=BE=0A=00=00=00=89t$=04=E8=07S=FF= +=FF=EB=85=89D$=0C=B8t=8F=05=08=89L$=08=89D$=04=89=1C$=E8\S=FF=FF=E9+=FF=FF= +=FF=90=90=90=90=90=90=90=90=90=90=90=90=90=90=90U=89=E5=83=EC=18=A1=E4=A9= +=05=08=85=C0uQ=C7=04$=00=00=00=00=B9{=8F=05=08=B8=05=00=00=00=89L$=04=89D= +$=08=E8;S=FF=FF=89D$=0C=BAN=87=05=081=C0=89D$=04=A10=A3=05=08=89T$=08=89=04= +$=E8[W=FF=FF=C7=04$=01=00=00=00=E8?V=FF=FF=8D=B4&=00=00=00=00=FF=D0=EB=AB= +=8D=B6=00=00=00=00=8D=BF=00=00=00=00U=89=E5=83=EC=08=8BE=08=89=04$=E8=97S= +=FF=FF=85=C0t=04=89=EC]=C3=E8r=FF=FF=FF=89=F6U=89=E5=83=EC=08=8BU=08=8BE=0C= +=89=14$=89D$=04=E8=C0T=FF=FF=85=C0t=04=89=EC]=C3=E8K=FF=FF=FF=8Dt&=00=8D=BC= +'=00=00=00=00U=89=E5=83=EC=08=8BE=0C=89D$=04=8BE=08=89=04$=E8=D0U=FF=FF=85= +=C0t=04=89=EC]=C3=E8=1B=FF=FF=FF=90=90=90=90=90=90=90=90=90=90=90U=89=E5W= +VS=83=EC=0C=BB=80=00=00=00=8Bu=08=EB=0D=90=90=90=90=90=90=90=90=90=90=90=90= +=90=89=1C$=E8X=FF=FF=FF=89D$=04=89=C7=89\$=08=894$=E8>Q=FF=FF=85=C0x%9=D8= +r=13=89<$=E8=8EU=FF=FF=8D=04=1B=85=C0=89=C3y=CD=EB%=C6=048=00=89=F8=83=C4= +=0C[^_]=C3=E8pR=FF=FF=8B0=89=C3=89<$=E8dU=FF=FF=8931=C0=EB=E1=E8=91=FE=FF= +=FF=90U=89=E5=83=EC=18=89]=FC=8B]=08=89=1C$=E8=14S=FF=FF@=89=04$=E8=E3=FE= +=FF=FF=89=04$=89\$=04=E8?V=FF=FF=8B]=FC=89=EC]=C3U=89=E5=83=EC=10=89u=F8=8B= +M=0C=8Bu=08=89]=F4=89}=FC=8B>=89=FB=0F=AF=D9=89=D8=99=F7=F9=B9=01=00=00=00= +9=C7=89E=F0t=13=8B]=F4=89=C8=8Bu=F8=8B}=FC=89=EC]=C3=8Dt&=00=89=1E1=C9=EB= +=E7=8Dv=00=8D=BC'=00=00=00=00U=89=E5WVS=83=EC=0C=8B]=10=8B}=08=8Bu=0CK=83= +=FB=FFt#=90=8D=B4&=00=00=00=00=89t$=04=89<$=E8=84=FF=FF=FF=85=C0=BA=01=00= +=00=00u=08K=83=FB=FFu=E51=D2=83=C4=0C=89=D0[^_]=C3=89=F6=8D=BC'=00=00=00=00= +U=89=E5WVS=83=EC=1C=8Bu=10=83=FE$=0F=87'=02=00=00=8B}=0C=85=FF=0F=84=14=02= +=00=00=E8cQ=FF=FF=C7=00=00=00=00=00=89=C31=C0=89D$=0C=8BE=08=89t$=08=89|$= +=04=89=04$=E8RR=FF=FF=89E=EC=8B=03=85=C0=0F=85=D7=01=00=00=8B=1F;]=08=0F=84= +=92=01=00=00=8B]=18=85=DB=0F=84=B7=00=00=00=8B=1F=0F=B6=03=84=C0=0F=84=AA= +=00=00=00=C7E=E8=01=00=00=00=0F=BE=C0=BE=00=04=00=00=89D$=04=8BE=18=89=04= +$=E8=F4O=FF=FF=85=C0=0F=84=02=01=00=00=8BE=18=B90=00=00=00=89L$=04=89=04$= +=E8=D8O=FF=FF=85=C0t,=0F=BEC=01=83=F8D=0F=84=FD=00=00=00=83=F8D=0F=8F=05=01= +=00=00=83=F8B=0F=84=EB=00=00=00=8D=B6=00=00=00=00=8D=BC'=00=00=00=00=0F=BE= +=03=83=E8B=83=F85=0F=87=AF=00=00=00=FF$=85=D8=8F=05=08=BA=00=02=00=00=89T= +$=04=8DE=EC=89=04$=E8V=FE=FF=FF=8D=B6=00=00=00=00=89=C2=85=D2=B8=03=00=00= +=00u=0F=8BE=E8=01=07=8BE=EC=8BU=14=89=021=C0=83=C4=1C[^_]=C3=B8=00=04=00=00= +=89D$=04=EB=C2=B8=06=00=00=00=8D=B4&=00=00=00=00=8D=BC'=00=00=00=00=89D$=08= +=89t$=04=8DE=EC=89=04$=E8M=FE=FF=FF=EB=AB=B8=05=00=00=00=EB=E4=BA=08=00=00= +=00=89T$=08=EB=DD=B8=07=00=00=00=EB=D2=B8=03=00=00=00=EB=CB=B8=01=00=00=00= +=EB=C4=B8=02=00=00=00=EB=BD=BB=04=00=00=00=89\$=08=EB=B6=8BE=EC=8BU=14=89= +=02=B8=02=00=00=00=E9z=FF=FF=FF=B9=02=00=00=00=89L$=04=E9A=FF=FF=FF=C7E=E8= +=02=00=00=00=BE=E8=03=00=00=E9=11=FF=FF=FF=83=F8i=0F=85=08=FF=FF=FF=80{=02= +B=0F=85=FE=FE=FF=FF=C7E=E8=03=00=00=00=E9=F2=FE=FF=FF=89=F6=8Bu=18=85=F6t= +)=0F=B6=03=84=C0t"=8BU=18=0F=BE=C0=89D$=04=89=14$=E8xN=FF=FF=85=C0t=0C=C7= +E=EC=01=00=00=00=E9>=FE=FF=FF=B8=01=00=00=00=E9=00=FF=FF=FF=B8=03=00=00=00= +=E9=F6=FE=FF=FF=8D}=F0=E9=E4=FD=FF=FF=C7=04$=A0=8F=05=08=B8=C6=8F=05=08=89= +D$=0C=B8v=00=00=00=89D$=08=B8=CE=8F=05=08=89D$=04=E8=D5N=FF=FF=90=90=90=90= +=90=90=90=90=90=90=90=90=90U=89=E5WVS=83=EC=1C=8BE=0C=8BM=08=99=89D$=08=8B= +D$=08=89T$=0C=8B=19=8B|$=0C=8Bq=04=F7=E3=0F=AF=FB=89E=E8=89=D0=01=F8=8B|$= +=08=8BU=E8=0F=AF=FE=89=14$=01=F8=89E=EC=8BM=EC=89L$=04=E8A=0F=00=00=89=F1= +1=D81=D1=09=C1=BA=01=00=00=00u=10=8BU=EC=8BM=08=8BE=E8=89Q=041=D2=89=01=83= +=C4=1C=89=D0[^_]=C3=90=8D=B4&=00=00=00=00U=89=E5WVS=83=EC=0C=8B]=10=8B}=08= +=8Bu=0CK=83=FB=FFt#=90=8D=B4&=00=00=00=00=89t$=04=89<$=E8T=FF=FF=FF=85=C0= +=BA=01=00=00=00u=08K=83=FB=FFu=E51=D2=83=C4=0C=89=D0[^_]=C3=89=F6=8D=BC'=00= +=00=00=00U=89=E5WVS=83=EC,=8Bu=08=83}=10$=0F=87@=02=00=00=8B}=0C=85=FF=0F= +=84-=02=00=00=E8=12R=FF=FF=8B=08=89=F3=8D=B6=00=00=00=00=0F=B6=13=0F=B6=C2= +=F6DA=01 t=03C=EB=F0=80=FA-=B8=01=00=00=00=0F=84=FF=00=00=00=E8=F5M=FF=FF= +=C7=00=00=00=00=00=89=C31=C0=89D$=0C=8BE=10=89|$=04=894$=89D$=08=E8=D4N=FF= +=FF=89E=E0=8B=03=89U=E4=85=C0=0F=85=C0=01=00=00=8B=1F9=F3=0F=84u=01=00=00= +=8B]=18=85=DB=0F=84=A4=00=00=00=8B=1F=0F=B6=03=84=C0=0F=84=97=00=00=00=C7= +E=DC=01=00=00=00=0F=BE=C0=BE=00=04=00=00=89D$=04=8BE=18=89=04$=E8=84L=FF=FF= +=85=C0=0F=84=E7=00=00=00=8BE=18=B90=00=00=00=89L$=04=89=04$=E8hL=FF=FF=85= +=C0t=1F=0F=BEC=01=83=F8D=0F=84=E2=00=00=00=83=F8D=0F=8F=EA=00=00=00=83=F8= +B=0F=84=D0=00=00=00=0F=BE=03=83=E8B=83=F85=0F=87=A1=00=00=00=FF$=85=F4=90= +=05=08=BA=00=02=00=00=89T$=04=8DE=E0=89=04$=E8=03=FE=FF=FF=89=C2=85=D2=B8= +=03=00=00=00u=15=8BE=DC=01=07=8BE=E0=8BU=E4=8BM=14=89=011=C0=89Q=04=83=C4= +,[^_]=C3=B8=00=04=00=00=89D$=04=EB=C2=B8=06=00=00=00=89D$=08=89t$=04=8DE=E0= +=89=04$=E88=FE=FF=FF=EB=B3=B8=05=00=00=00=EB=E4=BA=08=00=00=00=89T$=08=EB= +=DD=B8=07=00=00=00=EB=D2=B8=03=00=00=00=EB=CB=B8=01=00=00=00=EB=C4=B8=02=00= +=00=00=EB=BD=BB=04=00=00=00=89\$=08=EB=B6=8BE=E0=8BM=14=8BU=E4=89=01=B8=02= +=00=00=00=EB=85=B9=02=00=00=00=89L$=04=E9O=FF=FF=FF=C7E=DC=02=00=00=00=BE= +=E8=03=00=00=E9=1F=FF=FF=FF=83=F8i=0F=85=16=FF=FF=FF=80{=02B=0F=85=0C=FF=FF= +=FF=C7E=DC=03=00=00=00=E9=00=FF=FF=FF=8Bu=18=85=F6t0=0F=B6=03=84=C0t)=8BM= +=18=0F=BE=C0=89D$=04=89=0C$=E8%K=FF=FF=85=C0t=13=C7E=E0=01=00=00=00=C7E=E4= +=00=00=00=00=E9T=FE=FF=FF=B8=01=00=00=00=E9=09=FF=FF=FF=B8=03=00=00=00=E9= +=FF=FE=FF=FF=8D}=EC=E9=CB=FD=FF=FF=C7=04$=A0=8F=05=08=B8=E6=90=05=08=89D$= +=0C=B8v=00=00=00=89D$=08=B8=CE=8F=05=08=89D$=04=E8{K=FF=FF=90=90=90U=89=E5= +=83=EC=18=89]=F4=8BE=08=8B]=0C=89u=F8=89}=FC=89=04$=E8=FBJ=FF=FF=89=C61=C0= +=85=F6t=0C=FC=B9=0B=00=00=00=89=DF=F3=A5=89=D8=8B]=F4=8Bu=F8=8B}=FC=89=EC= +]=C3=89=F6U=89=E5WVS=83=EC=14=8BE =85=C0=0F=84>=01=00=00=8BP=14=8BX=1C=89= +U=F0=8BP=08=89]=EC=8BX=04=8B=00=89U=E8=89]=E4=89E=E0=8BE=08=C1=F8=02=F6E=08= +=03=8D=B0=DB=01=00=00u=06=8D=B0=DA=01=00=00=8BE=F0=C1=F8=02=8D=B8=DB=01=00= +=00=F6E=F0=03u=06=8D=B8=DA=01=00=00=BB=1F=85=EBQ=89=F0=89=F1=F7=EB=89=F0=C1= +=F8=1F)=FE=C1=FA=03)=C2=8D=04=92=8D=04=80)=C1=C1=E9=1F=89=F8)=CA=89=D1=F7= +=EB=89=F8=89=FB=C1=F8=1F=8B}=0C=C1=FA=03)=C2=8D=04=92=8D=04=80)=C3=89=D8=C1= +=E8=1F)=C2=89=CB=89=D0=C1=FB=02)=C1=8BE=F0=C1=FA=02)=CE)=D3=8BU=08=8D=1C3= +=8Bu=EC=8BM=E4)=C2=8D=04=D2=8D=04=C2=8D=04=80=8BU=10=01=F8)=F0=01=D8=8B]=E8= +=8D=04@=8D=04=C2)=D8=89=C2=C1=E2=04)=C2=8BE=14=8D=14=90)=CA=8BE=18=89=D1=C1= +=E1=04)=D1=8D=0C=88=8BE=E0)=C1=8BE=1C=8B=181=C0=8D=14=199=DA=0F=9D=C0=C1=E9= +=1F9=C8u=16=85=DBx=1E1=C0=81=FB=FF=FF=FF=7F=0F=95=C0=05=FE=FF=FF=7F=89=C2= +=83=C4=14=89=D0[^_]=C3=89=F61=C0=81=FB=00=00=00=80=0F=94=C0-=00=00=00=80=EB= +=E0=8BU=1C=8B=1A=EB=C5=8D=B4&=00=00=00=00U=89=E5WVS=83=EC\=8BU=0C=8BE=10=89= +=14$=89D$=04=FFU=08=85=C0=89=C2t=0A=83=C4\=89=D0[^_]=C3=8BM=0C=8B=01=85=C0= +t=ED=89=C31=FF=8Dv=00=8D=BC'=00=00=00=00=85=DB=8DG=FFx=03=8DG=019=C3tR=85= +=DBxC=89=D8)=F8=D1=F8=8D=048=89E=B4=8Bu=0C=8BU=10=8BE=B4=89=06=89T$=04=89= +4$=FFU=08=85=C0=89=C2t=17=FC=B9=0B=00=00=00=8D}=B8=89=C6=F3=A5=8B}=B4=EB=B5= +=90=8Dt&=00=8B]=B4=EB=AB=89=F8)=D8=D1=F8=8D=04=18=EB=BB=85=D2=0F=85w=FF=FF= +=FF=85=FF=0F=84o=FF=FF=FF=FC=8BE=0C=B9=0B=00=00=00=8BU=10=898=8DE=B8=8B}=10= +=89=C6=F3=A5=E9R=FF=FF=FF=8Dv=00U=89=E5W=BF=06=00=00=00V1=F6S=81=EC=DC=00= +=00=00=8BE=08=89=BDx=FF=FF=FF=8BU=08=8BM=08=8B=00=8BR=04=8BI=08=89=85t=FF= +=FF=FF=8BE=08=89=95p=FF=FF=FF=8BU=08=8B@=0C=89=8Dl=FF=FF=FF=8BJ=10=8Bz=14= +=89=85h=FF=FF=FF=8BB =89=85d=FF=FF=FF=B8=AB=AA=AA*=F7=E9=89=C8=C1=F8=1F=D1= +=FA)=C2=8D=04R=C1=E0=02)=C1=89=CB=C1=EB=1F)=DA=8D=14:=F6=C2=03=89=95\=FF=FF= +=FFu6=B8=1F=85=EBQ=F7=EA=8B=85\=FF=FF=FF=C1=FA=05=C1=F8=1F)=C2=8D=04=92=8D= +=04=80=C1=E0=029=85\=FF=FF=FF=0F=85{=04=00=00=89=D0=83=E0=03H=0F=84o=04=00= +=00=8D=146=01=F2=8D=04=1B=8D=14=96=01=D8=8D=04=82=01=C8=8B=95h=FF=FF=FF=8B= +M=10=0F=B7=84=00=E0=91=05=08=8B=09=8DD=02=FF=89=85X=FF=FF=FF=8B=85t=FF=FF= +=FF=89=8DT=FF=FF=FF=89=85P=FF=FF=FF=8B=95P=FF=FF=FF=C1=E8=1FH!=D0=83=F8;=89= +=85t=FF=FF=FF~=0B=BE;=00=00=00=89=B5t=FF=FF=FF=8B=B5T=FF=FF=FF=8B=85\=FF=FF= +=FF=F7=DE=C1=F8=02=8D=98=DB=01=00=00=F6=85\=FF=FF=FF=03u=06=8D=98=DA=01=00= +=00=C1=BDh=FF=FF=FF=0A=89=D8=BA=1F=85=EBQ=F7=EA=89=D9=89=D8=C1=F8=1F=D1=FF= +=C1=FA=03)=C2=8D=04=92=8D=04=80)=C1=89=C8=C1=E8=1F)=C2)=D3=89=D1=8B=95\=FF= +=FF=FF=C1=F9=02=8D=8C=0B#=FE=FF=FF=8B=9DX=FF=FF=FF=83=EAF=8D=04=D2=8D=04=C2= +=8D=04=80=8B=95l=FF=FF=FF=01=D8=01=C8=8D=04@=8D=04=C2=8B=8Dp=FF=FF=FF=89=C2= +=C1=E2=04)=C2=8D=14=91=89=D0=8B=8Dh=FF=FF=FF=C1=E0=04)=D0=8B=95t=FF=FF=FF= +=8D=04=82=8B=95l=FF=FF=FF)=F0=89=85|=FF=FF=FF=8DD9=DD=C1=FA=0E=01=D0=8B=95= +p=FF=FF=FF=C1=FA=14=01=D0=8B=95|=FF=FF=FF=C1=FA=1A)=C2=0F=88#=03=00=00=BB= +=15=00=00=009=D3}8=8B=8D|=FF=FF=FF=F7=D1=89=CA=C1=FA=1A)=C2=0F=88=FE=02=00= +=009=D3=BE=FF=FF=FF=FF=0F=8C=D0=00=00=00=8B=95|=FF=FF=FF=89=C8=89=8D|=FF=FF= +=FF)=D0=01=85T=FF=FF=FF=8B=B5|=FF=FF=FF1=C0=89=85`=FF=FF=FF=89=F7=89u=84=8D= +v=00=8D=BC'=00=00=00=00=8BM=0C=8DU=84=8DE=B8=89T$=04=89=0C$=89D$=08=E8=B7= +=FC=FF=FF=89D$=18=8B=95p=FF=FF=FF=8DE=84=89D$=14=8B=8Dl=FF=FF=FF=8B=85t=FF= +=FF=FF=89T$=0C=8B=95\=FF=FF=FF=89D$=10=8B=85X=FF=FF=FF=89L$=08=89=14$=89D= +$=04=E8=16=FB=FF=FF=8B]=84=89=C19=C3=89=9DL=FF=FF=FF=0F=843=01=00=009=FBt= +?=FF=8Dx=FF=FF=FFt=1F=89M=841=C0=89=F7=83}=D8=00=8B=B5L=FF=FF=FF=0F=95=C0= +=89=85`=FF=FF=FF=E9h=FF=FF=FF=BE=FF=FF=FF=FF=81=C4=DC=00=00=00=89=F0[^_]=C3= +=8D=B6=00=00=00=009=F3t=BD=8BU=D8=85=D2x$=8B=85d=FF=FF=FF=85=C0=0F=88=BA=00= +=00=00=8B=BDd=FF=FF=FF=85=FF=0F=95=C0=85=D2=0F=95=C21=D0=A8=01t=92=8B=85T= +=FF=FF=FF=8B=B5|=FF=FF=FF=8BU=10=01=D8)=F0=89=02=8BE=B89=85P=FF=FF=FFth=8B= +=8Dt=FF=FF=FF1=D2=85=C9u=05=83=F8 =00%*s =00%3lu, %3l= +u =00=0AReport bugs to <%s>.=0A=00bug-coreutils@gnu.org=00=00=00=00=00=08= +Z=05=08=A6]=05=08=AB]=05=08[\=05=08=00=00=00=00=00=00=00=00=01=00=00=00=02= +=00=00=00=03=00=00=00=00=00=00=00=11Z=05=08=16Z=05=08=1FZ=05=08=00=00=00=00= +=00=00=00=00=01=00=00=00=02=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00)Z=05=08,Z=05=08/Z=05=082Z=05= +=085Z=05=088Z=05=08;Z=05=08>Z=05=08=AC]=05=08AZ=05=08DZ=05=08GZ=05=08=8B=8A= +=05=08JZ=05=08MZ=05=08=00=00=00=00=0A[=05=08=00=00=00=00=00=00=00=00a=00=00= +=00=89Z=05=08=00=00=00=00=00=00=00=00b=00=00=00=90Z=05=08=00=00=00=00=00=00= +=00=00d=00=00=00=9AZ=05=08=00=00=00=00=00=00=00=00D=00=00=00=A0Z=05=08=00= +=00=00=00=00=00=00=00=85=00=00=00=AAZ=05=08=00=00=00=00=00=00=00=00h=00=00= +=00=B9Z=05=08=00=00=00=00=00=00=00=00i=00=00=00=BFZ=05=08=00=00=00=00=00=00= +=00=00k=00=00=00=C9Z=05=08=00=00=00=00=00=00=00=00n=00=00=00=D9Z=05=08=00= +=00=00=00=00=00=00=00G=00=00=00=E2Z=05=08=00=00=00=00=00=00=00=00q=00=00=00= +=F5Z=05=08=00=00=00=00=00=00=00=00r=00=00=00=08=8B=05=08=00=00=00=00=00=00= +=00=00s=00=00=00=FDZ=05=08=01=00=00=00=00=00=00=00w=00=00=00=03[=05=08=00= +=00=00=00=00=00=00=00A=00=00=00=0E[=05=08=00=00=00=00=00=00=00=00B=00=00=00= +=16Z=05=08=00=00=00=00=00=00=00=00F=00=00=00=1FZ=05=08=00=00=00=00=00=00=00= +=00p=00=00=00=1D[=05=08=00=00=00=00=00=00=00=00=89=00=00=00 [=05=08=00=00= +=00=00=00=00=00=00H=00=00=00 j=05=08=00=00=00=00=00=00=00=00=83=00=00=009= +[=05=08=01=00=00=00=00=00=00=00I=00=00=00=09]=05=08=01=00=00=00=00=00=00=00= +=86=00=00=00@[=05=08=00=00=00=00=00=00=00=00L=00=00=00L[=05=08=00=00=00=00= +=00=00=00=00N=00=00=00T[=05=08=00=00=00=00=00=00=00=00Q=00=00=00=1B]=05=08= +=01=00=00=00=00=00=00=00=87=00=00=00_[=05=08=00=00=00=00=00=00=00=00R=00=00= +=00=00]=05=08=01=00=00=00=00=00=00=00=84=00=00=00i[=05=08=00=00=00=00=00=00= +=00=00=88=00=00=00+]=05=08=01=00=00=00=00=00=00=00=8A=00=00=00|[=05=08=01= +=00=00=00=00=00=00=00T=00=00=00=A5Z=05=08=01=00=00=00=00=00=00=00=8B=00=00= +=00=84[=05=08=01=00=00=00=00=00=00=00=8C=00=00=00=F8\=05=08=02=00=00=00=00= +=00=00=00=82=00=00=00=8F[=05=08=01=00=00=00=00=00=00=00=81=00=00=00=9A[=05= +=08=00=00=00=00=00=00=00=00=80=00=00=00=A1[=05=08=00=00=00=00=00=00=00=00= +~=FF=FF=FF=A6[=05=08=00=00=00=00=00=00=00=00}=FF=FF=FF=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=AE[=05=08=B6[=05=08=BB[=05=08=C2[=05=08=CD[=05= +=08=D4[=05=08=DD[=05=08=00=00=00=00=00=00=00=00=00=00=00=00=04=00=00=00=03= +=00=00=00=03=00=00=00=02=00=00=00=01=00=00=00=11Z=05=08=A5Z=05=08=08=8B=05= +=08=EB[=05=08=A6[=05=08=00=00=00=00=00=00=00=00=03=00=00=00=04=00=00=00=02= +=00=00=00=05=00=00=00=F5[=05=08=FB[=05=08=02\=05=08=06\=05=08=0C\=05=08=00= +=00=00=00=02=00=00=00=02=00=00=00=02=00=00=00=01=00=00=00=01=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00s=8B=05=08=13\=05=08=17\=05=08=1D\=05=082Z=05= +=08=11Z=05=08#\=05=08+\=05=08(\=05=08=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=01=00=00=00=01=00=00=00=01= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=02=00=00=00=02=00=00=00=02=00= +=00=00=01=00=00=00=02=00=00=00=0D=00=00=00=03=00=00=00=0F=00=00=00=14=00=00= +=00posix-=00=00+=A6=04=08=81=A6=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04= +=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08= +@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA= +=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04= +=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08= +@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA= +=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04= +=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08= +@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA= +=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04= +=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08= +@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA= +=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04= +=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08= +@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA= +=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04= +=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08= +@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA= +=04=08=D0=A5=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04= +=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08= +@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA= +=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04= +=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08= +@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA= +=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08=9A=A6=04=08@=AA=04= +=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08= +@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08=B6= +=A6=04=08=CC=A6=04=08=E9=A6=04=08=F0=A6=04=08@=AA=04=08=FF=A6=04=08=0E=A7= +=04=08=1B=A7=04=08+=A7=04=08@=AA=04=08@=AA=04=085=A7=04=08@=AA=04=08D=A7=04= +=08@=AA=04=08@=AA=04=08[=A7=04=08b=A7=04=08q=A7=04=08=87=A7=04=08=0E=A8=04= +=08@=AA=04=08@=AA=04=08=15=A8=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08= +@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08=14=A6=04=08=1F=A8=04=08)=A8=04=08= +8=A8=04=08@=AA=04=08G=A8=04=08=A7=A8=04=08=BA=A8=04=08=ED=A8=04=08@=AA=04= +=08=FC=A8=04=08#=A9=04=08*=A9=04=084=A9=04=08@=A9=04=08L=A9=04=08V=A9=04=08= +e=A9=04=08u=A9=04=08=85=A9=04=08=95=A9=04=08=9F=A9=04=08=A9=A9=04=080=AA=04= +=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08@=AA=04=08= +L=AA=04=08X=AA=04=08=97=AA=04=08=1B=AB=04=08+=AB=04=08v=AB=04=08=8A=AB=04= +=08=CF=AB=04=08=14=AC=04=08=1B=AC=04=08%=AC=04=08j=AC=04=08=AF=AC=04=08=97= +=B1=04=08=12=B2=04=08=A0=B2=04=08=C2=B2=04=08=04=B3=04=083=B2=04=08=90=B2= +=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04= +=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08= +=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90= +=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2= +=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04= +=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08= +=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90= +=B2=04=08=90=B2=04=08=90=B2=04=08$=B2=04=08$=B2=04=08$=B2=04=08$=B2=04=08= +$=B2=04=08$=B2=04=08$=B2=04=08$=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04= +=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08:=B2=04=08=90=B2=04=08= +=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90= +=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2= +=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04= +=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=95=B2=04=08=90=B2=04=08= +=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08O=B2=04=08=90= +=B2=04=08V=B2=04=08]=B2=04=08=90=B2=04=08=90=B2=04=08d=B2=04=08k=B2=04=08= +=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08=90= +=B2=04=08r=B2=04=08=90=B2=04=08=90=B2=04=08=90=B2=04=08y=B2=04=08=90=B2=04= +=08=80=B2=04=08=90=B2=04=08=87=B2=04=08=90=B2=04=08=95=B2=04=08=D7=B2=04=08= +=D7=B2=04=08=D7=B2=04=08=D7=B2=04=08=D7=B2=04=08=D7=B2=04=08=D7=B2=04=08=D7= +=B2=04=08=D7=B2=04=08=D7=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2= +=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=E6=B2=04=08=E6=B2=04=08=E6=B2=04= +=08=E6=B2=04=08=E6=B2=04=08=E6=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08= +=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7= +=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2= +=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04= +=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=B7=B2=04=08=F5=B2=04=08=F5=B2=04=08= +=F5=B2=04=08=F5=B2=04=08=F5=B2=04=08=F5=B2=04=08i=CE=04=08t=CF=04=08=91=CF= +=04=08=D0=CD=04=08=B0=CF=04=08=D0=CF=04=08s=D0=04=08=0D=D0=04=08=C9=D0=04= +=08=DC=D0=04=08=EF=D0=04=08=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00dereference-command-line-symlink-to-d= +ir=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00sizeof (struct dev_ino) <=3D __extension__ ({ struct obstack const *__= +o =3D (&dev_ino_obstack); (unsigned) (__o->next_free - __o->object_base);= + })=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00hash_get_n_entri= +es (active_dir_set) =3D=3D 0=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00//DIRED-OPTIONS// --quoting-style=3D%s=0A=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UX1=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00Richard Stallman and David MacKenzie=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00ignoring invalid tab size in environment variable TABSIZE: %s=00=00=00= +ignoring invalid width in environment variable COLUMNS: %s=00=00=00=00=00= +=00ignoring invalid value of environment variable QUOTING_STYLE: %s=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00unparsable value for LS_COLORS environment variable=00=00=00= +=00=00=00=00=00=00=00=00=00=00not listing already-listed directory: %s=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00cann= +ot determine device and inode of %s=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00Try `%s --help' for more information.= +=0A=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00Usage: %s [OPTION]... [FILE]...=0A=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00List infor= +mation about the FILEs (the current directory by default).=0ASort entries= + alphabetically if none of -cftuSUX nor --sort.=0A=0A=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00Mand= +atory arguments to long options are mandatory for short options too.=0A=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00 -a, -= +-all do not hide entries starting with .=0A -A, --almos= +t-all do not list implied . and ..=0A --author = + print the author of each file=0A -b, --escape print oct= +al escapes for nongraphic characters=0A=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00 --block-si= +ze=3DSIZE use SIZE-byte blocks=0A -B, --ignore-backups do not= + list implied entries ending with ~=0A -c with -= +lt: sort by, and show, ctime (time of last=0A = + modification of file status information)=0A = + with -l: show ctime and sort by name=0A = + otherwise: sort by ctime=0A=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00 -C list entries by columns=0A= + --color[=3DWHEN] control whether color is used to distingui= +sh file=0A types. WHEN may be `never', `al= +ways', or `auto'=0A -d, --directory list directory entries in= +stead of contents,=0A and do not dereferenc= +e symbolic links=0A -D, --dired generate output designed = +for Emacs' dired mode=0A=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00 -f do not sort, enable -aU, disable -lst=0A = + -F, --classify append indicator (one of */=3D@|) to entries=0A= + --format=3DWORD across -x, commas -m, horizontal -x, long = +-l,=0A single-column -1, verbose -l, vertic= +al -C=0A --full-time like -l --time-style=3Dfull-iso=0A=00= +=00=00=00=00=00 -g like -l, but do not list owne= +r=0A -G, --no-group inhibit display of group information=0A = + -h, --human-readable print sizes in human readable format (e.g., 1K 234= +M 2G)=0A --si likewise, but use powers of 1000 not= + 1024=0A -H, --dereference-command-line=0A f= +ollow symbolic links listed on the command line=0A --dereference-com= +mand-line-symlink-to-dir=0A follow each comma= +nd line symbolic link=0A that points to a d= +irectory=0A=00=00=00=00=00=00=00=00=00=00=00=00=00=00 --indicator-st= +yle=3DWORD append indicator with style WORD to entry names:=0A = + none (default), classify (-F), file-type (-p)=0A -i,= + --inode print index number of each file=0A -I, --ignore=3D= +PATTERN do not list implied entries matching shell PATTERN=0A -k = + like --block-size=3D1K=0A=00=00=00=00=00=00 -l = + use a long listing format=0A -L, --dereference = + when showing file information for a symbolic=0A = + link, show information for the file the link=0A = + references rather than for the link itself=0A -m = + fill width with a comma separated list of entries=0A=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00 = + -n, --numeric-uid-gid like -l, but list numeric UIDs and GIDs=0A -= +N, --literal print raw entry names (don't treat e.g. control= +=0A characters specially)=0A -o = + like -l, but do not list group information=0A -p, --file-t= +ype append indicator (one of /=3D@|) to entries=0A=00=00=00=00= +=00=00=00=00=00=00 -q, --hide-control-chars print ? instead of non gra= +phic characters=0A --show-control-chars show non graphic character= +s as-is (default=0A unless program is `ls' an= +d output is a terminal)=0A -Q, --quote-name enclose entry name= +s in double quotes=0A --quoting-style=3DWORD use quoting style WOR= +D for entry names:=0A literal, locale, shel= +l, shell-always, c, escape=0A=00=00=00=00=00=00=00=00=00=00=00=00=00 -r,= + --reverse reverse order while sorting=0A -R, --recursive = + list subdirectories recursively=0A -s, --size = +print size of each file, in blocks=0A=00=00=00=00=00=00=00=00=00=00 -S = + sort by file size=0A --sort=3DWORD = + extension -X, none -U, size -S, time -t,=0A = + version -v=0A status -c, time -t, atime -u,= + access -u, use -u=0A --time=3DWORD show time as WORD ins= +tead of modification time:=0A atime, access= +, use, ctime or status; use=0A specified ti= +me as sort key if --sort=3Dtime=0A=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00 --time-style=3DSTYLE show times usin= +g style STYLE:=0A full-iso, long-iso, iso, = +locale, +FORMAT=0A FORMAT is interpreted like= + `date'; if FORMAT is=0A FORMAT1FORM= +AT2, FORMAT1 applies to=0A non-recent files a= +nd FORMAT2 to recent files;=0A if STYLE is pr= +efixed with `posix-', STYLE=0A takes effect o= +nly outside the POSIX locale=0A -t sort by modif= +ication time=0A -T, --tabsize=3DCOLS assume tab stops at each CO= +LS instead of 8=0A=00=00=00=00=00=00=00=00=00=00=00=00=00=00 -u = + with -lt: sort by, and show, access time=0A = + with -l: show access time and sort by name=0A = + otherwise: sort by access time=0A -U = + do not sort; list entries in directory order=0A -v = + sort by version=0A=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00 -w, --width=3DCOLS assume= + screen width instead of current value=0A -x lis= +t entries by lines instead of by columns=0A -X s= +ort alphabetically by entry extension=0A -1 list= + one file per line=0A=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00 --help display this help and exit=0A=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00 --version output version= + information and exit=0A=00=00=00=00=00=00=00=00=00=00=00=0ASIZE may be (= +or may be an integer optionally followed by) one of following:=0AkB 1000,= + K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.=0A=00= +=00=00=00=00=00=00=0ABy default, color is not used to distinguish types o= +f files. That is=0Aequivalent to using --color=3Dnone. Using the --colo= +r option without the=0Aoptional WHEN argument is equivalent to using --co= +lor=3Dalways. With=0A--color=3Dauto, color codes are output only if stan= +dard output is connected=0Ato a terminal (tty).=0A=00preserving permissio= +ns for %s=00u::---,g::---,o::---=00setting permissions for %s=00%m/%d/%y=00= +%Y-%m-%d=00%H:%M:%S=00=F2=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00= +=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9= +=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04= +=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08= +=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00= +=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9= +=04=08=00=F9=04=08T=F8=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04= +=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08= +=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00= +=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9= +=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=FD=F9=04=08=FD=F9=04= +=08=95=FB=04=08V=FE=04=08=00=F9=04=08=D1=FF=04=08=E1=03=05=08=E1=FF=04=08= +=10=00=05=08=00=F9=04=08=00=F9=04=08=00=F9=04=08@=00=05=08p=00=05=08=00=F9= +=04=08=C8=00=05=08=00=F9=04=08=FE=00=05=08=0E=01=05=087=01=05=08G=01=05=08= +=E1=03=05=08=90=01=05=08=F0=01=05=08=02=02=05=08@=02=05=08=00=F9=04=08=00= +=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=00=F9=04=08=FD=F9=04=08=C3=06= +=05=08=F0=01=05=08h=03=05=08=92=03=05=08=00=F9=04=08=E1=03=05=08=C3=06=05= +=08=00=F9=04=08=ED=06=05=08=16=07=05=08@=07=05=08p=07=05=08=A0=07=05=08=00= +=F9=04=08=DE=00=05=08=00=F9=04=08"=FA=04=080=08=05=08=9C=08=05=08%=09=05=08= +=00=F9=04=08g=09=05=08=F0=01=05=08=91=09=05=08=E7=09=05=08=00=00=00=00=00= +=00=80?=CD=CCL?=F4=FD=B4?=00=00=00=00# entries: %u=0A=00# buckets= +: %u=0A=00max bucket length: %u=0A=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00# buckets = +used: %u (%.2f%%)=0A=00=00=00=C8Bambiguous argument %s for %s=00invali= +d argument %s for %s=00Valid arguments are:=00=0A - `%s'=00, `%s'=00writ= +e error=00%s: %s=00POSIX=00=00=00KMGTPEZY=00=00=00=AAZ=05=08=1D[=05=08=00= +=00=00=00p=00=00=00P=00=00=00=00=00=00=00=FF=FF=FF=FF=FF=FF=FF=FF>@=00=00= +=00=00=00=00=00=00=00=00=00=00=00=A0=02@=00=00=00=00=00=00=00=00=80_%.1Lf= +=00%.0Lf=00eEgGkKmMpPtTyYzZ0=00block size=00%s `%s' too large=00invalid %= +s `%s'=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00invalid chara= +cter following %s in `%s'=00shell=00shell-always=00clocale=00"=00`=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00L= +[=05=08g=8B=05=08m=8B=05=08=D6=8F=05=08=89Z=05=08[\=05=08z=8B=05=08=00=00= +=00=00=00=00=00=00=01=00=00=00=02=00=00=00=03=00=00=00=04=00=00=00=05=00=00= +=00=06=00=00=00=9E5=05=08=9E5=05=08=CF:=05=08t5=05=08=F0:=05=08=FC:=05=08= +=FC:=05=0806=05=08d8=05=08d8=05=08d8=05=08d8=05=08d8=05=08d8=05=08=007=05= +=08"7=05=08&7=05=08f7=05=08j7=05=08n7=05=08r7=05=08d8=05=08d8=05=08d8=05=08= +d8=05=08d8=05=08d8=05=08d8=05=08d8=05=08d8=05=08d8=05=08d8=05=08d8=05=08d= +8=05=08d8=05=08d8=05=08d8=05=08d8=05=08d8=05=08U:=05=08U:=05=08U:=05=08d:= +=05=08U:=05=08=906=05=08U:=05=08v7=05=08U:=05=08U:=05=08U:=05=08=906=05=08= +=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=90= +6=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05= +=08U:=05=08U:=05=08=906=05=08U:=05=08=CA7=05=08d8=05=08=906=05=08=906=05=08= +=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=90= +6=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05= +=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08= +=906=05=08=906=05=08U:=05=08L:=05=08=906=05=08U:=05=08=906=05=08U:=05=08=90= +6=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05= +=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08= +=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=90= +6=05=08=906=05=08=906=05=08=906=05=08=906=05=08U:=05=08=906=05=08d:=05=08= +=198=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=198=05=08=19= +8=05=08=198=05=08=906=05=08=906=05=08=906=05=08=198=05=08=906=05=08=198=05= +=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08= +=906=05=08=906=05=08=906=05=08=906=05=08=906=05=08=198=05=08=198=05=08=19= +8=05=08=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00Copyri= +ght (C) 2003 Free Software Foundation, Inc.=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00This is free software; see the source for copying conditio= +ns. There is NO=0Awarranty; not even for MERCHANTABILITY or FITNESS FOR = +A PARTICULAR PURPOSE.=0A=00%s (%s) %s=0A=00Written by %s.=0A=00%s %s=0A=00= +memory exhausted=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=000 <=3D strtol_base && strtol_base <=3D 36=00xstrtol=00xstrtol.c=00=E2= +D=05=08NE=05=08NE=05=08=EDD=05=08NE=05=08.E=05=08NE=05=08NE=05=08NE=05=08= +5E=05=08NE=05=08=94=04=08N=94=04=08^=94=04=08n=94=04=08~=94=04=08=8E=94=04= +=08=9E=94=04=08=AE=94=04=08=BE=94=04=08=CE=94=04=08=DE=94=04=08=EE=94=04=08= +=FE=94=04=08=0E=95=04=08=1E=95=04=08.=95=04=08>=95=04=08N=95=04=08^=95=04= +=08n=95=04=08~=95=04=08=8E=95=04=08=9E=95=04=08=AE=95=04=08=BE=95=04=08=CE= +=95=04=08=DE=95=04=08=EE=95=04=08=FE=95=04=08=0E=96=04=08=1E=96=04=08.=96= +=04=08>=96=04=08N=96=04=08^=96=04=08n=96=04=08~=96=04=08=8E=96=04=08=9E=96= +=04=08=AE=96=04=08=BE=96=04=08=CE=96=04=08=DE=96=04=08=EE=96=04=08=FE=96=04= +=08=0E=97=04=08=1E=97=04=08.=97=04=08>=97=04=08N=97=04=08^=97=04=08n=97=04= +=08~=97=04=08=8E=97=04=08=9E=97=04=08=AE=97=04=08=BE=97=04=08=CE=97=04=08= +=DE=97=04=08=EE=97=04=08=FE=97=04=08=0E=98=04=08=1E=98=04=08.=98=04=08>=98= +=04=08N=98=04=08^=98=04=08n=98=04=08~=98=04=08=8E=98=04=08=9E=98=04=08=AE= +=98=04=08=BE=98=04=08=CE=98=04=08=DE=98=04=08=EE=98=04=08=FE=98=04=08=0E=99= +=04=08=1E=99=04=08.=99=04=08>=99=04=08N=99=04=08d=A2=05=08=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00.shstrtab=00.interp=00.note.ABI-tag=00.hash=00.dynsym=00.dynstr=00= +=2Egnu.version=00.gnu.version_r=00.rel.dyn=00.rel.plt=00.init=00.text=00.= +fini=00.rodata=00.eh_frame_hdr=00.data=00.eh_frame=00.dynamic=00.ctors=00= +=2Edtors=00.jcr=00.got=00.bss=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00=00= +=00=00=0B=00=00=00=01=00=00=00=02=00=00=00=14=81=04=08=14=01=00=00=13=00=00= +=00=00=00=00=00=00=00=00=00=01=00=00=00=00=00=00=00=13=00=00=00=07=00=00=00= +=02=00=00=00(=81=04=08(=01=00=00 =00=00=00=00=00=00=00=00=00=00=00=04=00=00= +=00=00=00=00=00!=00=00=00=05=00=00=00=02=00=00=00H=81=04=08H=01=00=00(=03= +=00=00=04=00=00=00=00=00=00=00=04=00=00=00=04=00=00=00'=00=00=00=0B=00=00= +=00=02=00=00=00p=84=04=08p=04=00=00p=06=00=00=05=00=00=00=01=00=00=00=04=00= +=00=00=10=00=00=00/=00=00=00=03=00=00=00=02=00=00=00=E0=8A=04=08=E0=0A=00= +=00j=04=00=00=00=00=00=00=00=00=00=00=01=00=00=00=00=00=00=007=00=00=00=FF= +=FF=FFo=02=00=00=00J=8F=04=08J=0F=00=00=CE=00=00=00=04=00=00=00=00=00=00=00= +=02=00=00=00=02=00=00=00D=00=00=00=FE=FF=FFo=02=00=00=00=18=90=04=08=18=10= +=00=00=B0=00=00=00=05=00=00=00=03=00=00=00=04=00=00=00=00=00=00=00S=00=00= +=00=09=00=00=00=02=00=00=00=C8=90=04=08=C8=10=00=00(=00=00=00=04=00=00=00= +=00=00=00=00=04=00=00=00=08=00=00=00\=00=00=00=09=00=00=00=02=00=00=00=F0= +=90=04=08=F0=10=00=00=C0=02=00=00=04=00=00=00=0B=00=00=00=04=00=00=00=08=00= +=00=00e=00=00=00=01=00=00=00=06=00=00=00=B0=93=04=08=B0=13=00=00=17=00=00= +=00=00=00=00=00=00=00=00=00=04=00=00=00=00=00=00=00`=00=00=00=01=00=00=00= +=06=00=00=00=C8=93=04=08=C8=13=00=00=90=05=00=00=00=00=00=00=00=00=00=00=04= +=00=00=00=04=00=00=00k=00=00=00=01=00=00=00=06=00=00=00`=99=04=08`=19=00=00= +p=C0=00=00=00=00=00=00=00=00=00=00=10=00=00=00=00=00=00=00q=00=00=00=01=00= +=00=00=06=00=00=00=D0Y=05=08=D0=D9=00=00=1B=00=00=00=00=00=00=00=00=00=00= +=00=04=00=00=00=00=00=00=00w=00=00=00=01=00=00=00=02=00=00=00=00Z=05=08=00= +=DA=00=00=1C8=00=00=00=00=00=00=00=00=00=00 =00=00=00=00=00=00=00=7F=00=00= +=00=01=00=00=00=02=00=00=00=1C=92=05=08=1C=12=01=00,=00=00=00=00=00=00=00= +=00=00=00=00=04=00=00=00=00=00=00=00=8D=00=00=00=01=00=00=00=03=00=00=00`= +=A2=05=08`=12=01=00=E8=00=00=00=00=00=00=00=00=00=00=00 =00=00=00=00=00=00= +=00=93=00=00=00=01=00=00=00=02=00=00=00H=A3=05=08H=13=01=00=9C=00=00=00=00= +=00=00=00=00=00=00=00=04=00=00=00=00=00=00=00=9D=00=00=00=06=00=00=00=03=00= +=00=00=E4=A3=05=08=E4=13=01=00=D8=00=00=00=05=00=00=00=00=00=00=00=04=00=00= +=00=08=00=00=00=A6=00=00=00=01=00=00=00=03=00=00=00=BC=A4=05=08=BC=14=01=00= +=08=00=00=00=00=00=00=00=00=00=00=00=04=00=00=00=00=00=00=00=AD=00=00=00=01= +=00=00=00=03=00=00=00=C4=A4=05=08=C4=14=01=00=08=00=00=00=00=00=00=00=00=00= +=00=00=04=00=00=00=00=00=00=00=B4=00=00=00=01=00=00=00=03=00=00=00=CC=A4=05= +=08=CC=14=01=00=04=00=00=00=00=00=00=00=00=00=00=00=04=00=00=00=00=00=00=00= +=B9=00=00=00=01=00=00=00=03=00=00=00=D0=A4=05=08=D0=14=01=00t=01=00=00=00= +=00=00=00=00=00=00=00=04=00=00=00=04=00=00=00=BE=00=00=00=08=00=00=00=03=00= +=00=00`=A6=05=08`=16=01=00=90=03=00=00=00=00=00=00=00=00=00=00 =00=00=00=00= +=00=00=00=01=00=00=00=03=00=00=00=00=00=00=00=00=00=00=00`=16=01=00=C3=00= +=00=00=00=00=00=00=00=00=00=00=01=00=00=00=00=00=00=00 \ No newline at end of file diff --git a/tests/encoding/test-suites/encode/ls.uuencode b/tests/encoding/test-suites/encode/ls.uuencode new file mode 100644 index 00000000..37e82776 --- /dev/null +++ b/tests/encoding/test-suites/encode/ls.uuencode @@ -0,0 +1,1613 @@ +begin 644 no_name +M?T5,1@$! 0 ( P ! 8)D$"#0 D%P$ #0 ( ' +M "@ &0 8 8 T -( $"#2 ! C@ X 4 $ P !0! +M 4@00(%($$"!, 3 ! $ ! " ! @ @ 0(2!(! +M $@2 0 % ! $ !@$@$ 8*(%"&"B!0CD P D < 8 $ +M @ .03 0#DHP4(Y*,%"-@ #8 !@ 0 $ * $ "B!! @H +M@00(( " $ ! %#E=&0<$@$ ')(%"!R2!0@L + 0 +M $ +VQI8B]L9"UL:6YU>"YS;RXR $ $ $ !'3E4 +M ( " &$ !G #P [ ,0 +M %8 ^ +0 ? 2@ "L ] "\ C +M (0 $D !7 .0 P 4 $4 +M !> %L / 70 &$ "P !P !? %@ +M J "< "@ %P 3@ %H 9 +M!P $ !E -@ $P %D +M $, !8 8@ $( !/ & !1 * +M : "0 4@ #4 E ( $8 ; +M $ 8P !F . &0 " $0 !+ +M50 +M !$ 2 +M " 3 +M !0 +M$ !T L "8 B %P T +M P , -P +M /P I N 2 #H > "0 +M - 8 % %0 %, 8 $T +M $< 0 %0 #( 00 +M #@ #, =@$ -B3! @\ $@ 'P# +M #HDP0() $ !( S 0 ^),$""H 2 20 B4! @Z $@ +M )D" 8E 0(4P !( #U *)0$"/$ 2 U , #B4! AG 0 +M$@ '\! !(E 0(9@ !( !B Y*,%" 1 /'_ZP$ %B4! AQ +M $@ /\! !HE 0(D !( 5 @ >)0$"'P 2 N@( (B4 +M! @X $@ #H" "8E 0(G !( #A @ J)0$"#\ 2 ,@( +M +B4! @C $@ $)@$"%( 2 50 (B8! A* 0 $@ (# "8F 0( +M1 !( #U P J)@$"!D 2 > +B8! A' $@ '@" #( +MF 0(I@ !( "% P V)@$""0 2 @ ( .B8! BB $@ * ! +M !HI@4(! !$ %P"A P !%H%" 0 1 X # /B8! AO $@ +M /H" (F00(- !( D P &)D$"*P 2 &@ +M( #4# HF00(<0 !( !3 @ ;*8%" 0 1 !< Y , #B9! @0 +M 0 $@ "X " #A 2)D$"# 2 &QI8G)T +M+G-O+C$ 8VQO8VM?9V5T=&EM90!?2G9?4F5G:7-T97)#;&%S0!R96%D;&EN:P!?7V]V97)F;&]W &UB0!O<'1A7!E7W1O +M;&]W97)?;&]C %]O8G-T86-K7V)E9VEN &-A;&QO8P!W7!E +M7V=E=%]M8E]C=7)?;6%X &9P0!L;V-A;'1I;64 ;65M'-T +M870V- !?7V-T>7!E7V)?;&]C '-T'-T870V- !R86ES90!M8G-I;FET %]?8WAA7V%T97AI= !?961A=&$ +M7U]B HI@4(!U\ "RF!0@'8 ,*8%" =B TI@4(!V0 #BF!0@'9@ +M58GE@^P(Z,D% #H, 8 .C;Q0 R<, _S74I 4(_R78I 4( /\EW*0% +M"&@ Z>#_____)>"D!0AH" .G0_____R7DI 4(:! #IP/____\E +MZ*0%"&@8 Z;#_____)>RD!0AH( .F@_____R7PI 4(:"@ #ID/__ +M__\E]*0%"&@P Z8#_____)?BD!0AH. .EP_____R7\I 4(:$ #I +M8/____\E *4%"&A( Z5#_____)02E!0AH4 .E _____R4(I04(:%@ +M #I,/____\E#*4%"&A@ Z2#_____)1"E!0AH: .D0_____R44I04( +M:' #I /____\E&*4%"&AX Z?#^____)1RE!0AH@ .G@_O___R4@ +MI04(:(@ #IT/[___\E)*4%"&B0 Z<#^____)2BE!0AHF .FP_O__ +M_R4LI04(:* #IH/[___\E,*4%"&BH Z9#^____)32E!0AHL .F +M_O___R4XI04(:+@ #I*4%"&@X 0 +MZ7#]____)7RE!0AH0 $ .E@_?___R6 I04(:$@! #I4/W___\EA*4%"&A0 +M 0 Z4#]____)8BE!0AH6 $ .DP_?___R6,I04(:& ! #I(/W___\ED*4% +M"&AH 0 Z1#]____)92E!0AH< $ .D _?___R68I04(:'@! #I\/S___\E +MG*4%"&B 0 Z>#\____):"E!0AHB $ .G0_/___R6DI04(:) ! #IP/S_ +M__\EJ*4%"&B8 0 Z;#\____):RE!0AHH $ .F@_/___R6PI04(:*@! #I +MD/S___\EM*4%"&BP 0 Z8#\____);BE!0AHN $ .EP_/___R6\I04(:, ! +M #I8/S___\EP*4%"&C( 0 Z5#\____)<2E!0AHT $ .E _/___R7(I04( +M:-@! #I,/S___\ES*4%"&C@ 0 Z2#\____)="E!0AHZ $ .D0_/___R74 +MI04(:/ ! #I /S___\EV*4%"&CX 0 Z?#[____)=RE!0AH ( .G@^___ +M_R7@I04(: @" #IT/O___\EY*4%"&@0 @ Z<#[____)>BE!0AH& ( .FP +M^____R7LI04(:" " #IH/O___\E\*4%"&@H @ Z9#[____)?2E!0AH, ( +M .F ^____R7XI04(:#@" #I ( .GP^O___R4O&!7"F!0@!R<.)]E6)Y8/L"*',I 4(A6#[!B)7?R+%Q=P@0 QP0DP*<% +M"+GP____B4PD!.B^_?__BQ7,IP4(Z[O'!"1@:@4(N"]7UWITOC__XL$GT/'!"1 7 4(B40D!.AN^___.?-\Z.NN@$DH NEB____ +MC;0F %6)Y8/L&(M%"(M-#(M0!(L B4PD"#')B4PD#(D$)(E4) 3H&;L +M (GL7<.0C70F %6)Y8/L$(E]_(M]"(EU^(MU#(E=](M?!,9%\P"+!HM6!(L/ +MB=XQUC'("<9U(XMU#(M?#(M/"(M&"(M6#(G>,<@QU@G&=0K&1?,!C;8 +M#[9%\XM=](MU^(M]_(GL75=Z>_[__^-M"8 +M 58GE@^PHB5WTBU4,BT4(B77XBUT0BW44B7W\B57LB47HQP0D$ .AC +MI0 B1B)QXM5[(EP!(M%Z(E7#(E'"*&$I@4(B7PD!(D$).A/>@ A6# +M[ B)7?R+70B+ X7 =1^+0P2%P'4.B5T(BUW\B>Q=Z4'[__^)!"3H.?O__^OH +MB00DZ"_[___KUY"-="8 58GE@^P(QP0DH*(%".CN0P ARH B77\BW4(B5WXZ+G___^#_A1T +M1S' C9UH____B85H____C85L____B00DZ%#Z___'1>P ,<")1"0(B5PD +M!(DT).B']O__B30DZ"_Y__^+7?B+=?R)[%W#OA, #KYXVV %6)Y5=6 +M4X'LW $ +M%7 4(BU4,@^3PBP*)7"0$QP0D!@ */LJ04(Z"KW___'!"1& +M7 4(N5!J 0 H9BF!0B%P'1KBT@(AS^___'!"2 7 4( +MN&1BF!0B%P'1.H9"F!0B%P ^$*/[__^B6+@ BS68I@4(A?8/A)+^ +M__^A8*8%"(M0%#M0&',.Q@(*_T 4_P4HIP4(ZZV)!"2["@ (E<) 3HM/+_ +M_^OEQP0D15P%"#'_B7PD!.B8(P ZY['!"1B7 4(,<")1"0$Z'04 #IK_W_ +M_[A%7 4(BU4,,?^)1"0,N $ ")1"0(B7PD!(L$FD.)!"3H*!P #M="'S4 +MZ3?]___'!"1 IP4(N#B8! @Q_XE$)! Q]KB(E00(B40D#(E\) B)="0$Z$SR +M___'!"2 IP4(,<"Y.)@$"(E$) BZB)4$"#' B4PD$(E4) R)1"0$Z"+R___I +MG/S__\<$)!X QTKF@FP0(B4PD"+] G 0(OM";! B)?"00B70D#(E4) 3H +M.&\ *.$I@4(A< /A%8! #'!"3 IP4(N#B8! B)1"00N(B5! B)1"0,,<") +M1"0(,<")1"0$Z+GQ___IM?O__Z'HI@4(AB4\?__QP0D 0 .@X]/__A< /A-<, +M "X @ *.@I@4(N $ "C^*8%",<$)(9)FARF!0BC +M\*8%".NEN+I"F!0CII?[__Z%HI@4(B00D +MZZJX!0 */@I@4(Z8S^__\QP(E$) 3'!"0 Z!J- #I=?[__[@# +MZ^2X 0 */DI@4(Z5_^__^X! *.HI@4(QT7, 0 .E)_O__,< QR3'; +MB40D$(U%U(E$) RA:*8%"(E<) B)3"0$B00DZ .< "%P'41BT74AS[__^A:*8%"(D$).AODP QP0D (G#N=][__XM%V.NJ +MN , #I/O__[C I@4( +MB40D"+@! B40D!*%HI@4(B00DZ%F! "CO*8%"(L5Q*8%"*' I@4(B164 +MH@4(HY"B!0CI.?O__XL5:*8%"+@! A=)U.#';@_@!="J#^ )T%8D=T*8% +M"(7;#X00^___,<#I\?S__\<$) $ #HE>W__X7 =-N[ 0 .O4B50D!*$L +MHP4(N00 ")3"00QP0D]EP%"(E$)!2X@&(%"(E$) RX0&(%"(E$) CHWG +M (L$A8!B!0CKB[H$ B17@I@4(Z:7Z___'!"3^7 4(H2RC!0B)1"04N 0 +M ")1"00N,!A!0B)1"0,N*!A!0B)1"0(H6BF!0B)1"0$Z(QP "+%(7 804( +MB16@I@4(Z5KZ___'1= (6@4(,=N)':"F!0CI1OK__\<$) ==!0BA+*,%"(E$ +M)!2X! (E$)!"XN%X%"(E$) RXJ%X%"(E$) BA:*8%"(E$) 3H+7 (L$ +MA;A>!0CI-?O__\<$)!E=!0BA+*,%"+G BP4(B4PD#+J@BP4(NP0 ")7"00 +MB40D%*%HI@4(B50D"(E$) 3HZ&\ (L$A<"+!0CI,OO__S' Z4#]__^X4 +M .F:_/__QP0D*5T%"*$LHP4(N?1A!0B)3"0,NMQA!0B[! (E<)!")1"04 +MH6BF!0B)5"0(B40D!.B2;P BP2%]&$%".D,^___QP0D,%T%"*$LHP4(B40D +M%+@$ B40D$+@@8@4(B40D#+@(8@4(B40D"*%HI@4(B40D!.A-;P BP2% +M(&(%".E_^___H6BF!0B)1=#I%/G__\<$) #H"(< */\I@4(B00DZ'N' +M "#^ 0/A*," "AS*8%"(7 =#Z-F#9=!0@/MH V704(A,!T+9"-M"8 +M#[[ 0[X! B40D!*'\I@4(B70D"(D$).ARAP #[8#A,!UV\<$) #H +MGX8 *, IP4(N0$ "Z.@ (E,) B)5"0$B00DZ$"' "AR*8%"(7 #X09 +M @ BQ6@I@4(A=)T!S' H\BF!0BAI*8%"$B#^ %W%8M%S(7 =0Z%TG0.N , +M "CJ*8%"(72=4&+7="%VP^$N0$ (UV (D<)+\& OKQB!0B)?"0(B70D +M!.C>Z/__A!0B) +M1"0(QP0D/%T%".C3;0 BP2%E%X%"(/X 0^$A@ (/X 7)H@_@"=$F#^ -U +ME\<$) ( #HZ'$ (7 =(G__\8& (MUR(D]&*,%"(DU +M'*,%".F!_O__B?[KZ\<$))5=!0CH9N7__X7 B<,/A33^__^[H%T%".DJ_O__ +MBQ6@I@4(Z>W]__^X 0 +\@ B40D"*'\I@4(B7PD!(D$).CPA Z3G] +M___'!"2O704(Z!?E__^%P(G##X05]O__B1PD,<")1"00C473__XE$) @QR8E<) R)3"0$QP0D .CK +MZ/__Z9[U__^)'"0QP(E$)!"-1>R)1"0,,<")1"0(,<")1"0$Z%V3 "%P'41 +MBT7LA3__X7 #X2,]/__Z7'T__^)'"3HD(P ,<$) ")P[@% +MB40D"+B ; 4(B40D!.CHX___B5PD##';B40D"(E<) 3'!"0 Z [H___I +M^//__[@! HZ"F!0@QP.DG\___@_@##X7V\O__,<"CH*8%",<$) "X +M! (E$) 3H*X, .D"\___N@( ")%:"F!0CKV8GVC;PG %4QTHGE +M5U93@^P$BT4,QT7P (L8BT4(BS"-="8 ,?^-M"8 C;PG /\D +MO01G!0@/M@L/OL&#^#UT9X/X/7])A7UW#N/_____K\[\% Z]J0 +MC70F (/X7'0-@_A>=;:_! $/KCK\! Z_:+11"%P'2@Z](/M@,/OLB# +M^7AW<_\DC1AG!0@/OL"_ @ (U0T)!#ZXV_!@ .OVNG\ "0@_\!=>N( +M%C'_1O]%\.OANB #KZKH' Z^.Z" .O7UW#QT7, +X! 9L=%V#\_QD7: (D$ +M).A9CP H]2F!0B)1="0@_X"#X1X @ @_X"#X]! 0 @_X!#X2: A?9_ +MX87V>"J#/="B!0@&=:7\BS74H@4(O\%=!0BY!@ /.F=9"X 0 *.4I@4( +MZX3'!"0 N 4 ")1"0(N.!L!0B)1"0$Z-W@__^)1"0(,<")1"0$QP0D +M .@'Y?__H=2F!0B)!"3H&N3__XL=@*8%"(7;=!&)V(M;$(D$).@#Y/__ +MA=MU[S' H]"F!0CI;O___XM5U ^^ H/X*G0X@_@J?R.%P ^$5O___P^V KX" +M B$78C4(!D(UT)@")1=3I&/___X/X.G7@C4(!B474Z2/____'!"04 +MO@0 #H1XT (E%S(M5S*& I@4(_T74B4(0BT70B16 I@4(B4($N $ ") +M1"0(C474B40D!(U%T(D$).B/_/__BU7,B0*%P ^)LO[__^GZ_O__@_X#=$^# +M_@0/A;K^__^+5=2-0@&)1=2 .CUT"K[_____Z:+^__^+1="^ 0 (M5S(E" +M#(U%U#'2B40D!(U%T(E4) B)!"3H,?S__XM5S(E"".N?BU74OO____^-0@&) +M1=2 .CT/A8_^__^AX%X%"#';A!>!0B%P'7D@_[_#X4F_O__C478O\A=!0B)!"3HRX< (E\) 2)P[@% +M B40D",<$) #H*-___XE$) @QR8E<) R)3"0$QP0D .A.X___ +MZ=S]__^+5="XH*(%"+X! B538!#' B40D"(U%U(E$) 2-1=")!"3H:/O_ +M_XD$W:"B!0B%P ^)B?W__[[_____Z73___^+1=0/MA"$T@^$OOW__XA5V4"^ +M P .E#_O__C78 58GE@^P8B77XBW4,B5WTB7W\BWT(QP0D# .B/BP +MB<,QP(7V= B)-"3H?XP (E#!#' A?]T"(D\).ANC B0.AF*8%"(E#"(MU +M^(D=F*8%"(M]_(M=](GL7<.053' B>57,?]64X'L+ , (F]]/S__XF%\/S_ +M_^B'WO__QP B<.+10B)!"3HY.#__X7 B<"+5>2)1"0(BT6(B50D#(M5C(D$)(E4) 3H(^7__X7 #X78 +M! H="G!0B+%"+5>2) 8E1!.@O!@ Z,+=__^)QI"-M"8 QP8 B3PDZ)K< +M__^%P(G#='&)!"3HA 4 (7 =. /MD,2,=(\!G16/ )T4CP$=$X\ 71*/ IT +M1CP(=$(\#'0^C;0F (V\)P ")5"0$BT4(B40D##' B40D"(U#$XD$ +M).@D!@ 87P_/__$97T_/__ZX:-M@ /MM#KRXL>A=L/A<\# "%_W0, +MB3PDZ$W?__^%P'16BU4(B10DZ#:' #'!"0 N04 "ZX%T%"(E,) B) +MQHE4) 3HCMS__XG#Z.?<__^)="0,B5PD"(L QP0D (E$) 3HK>#__[@! +M HQRG!0CH-A4 *'DI@4(A< /A3$# "A#*<%"(7 #X3: H2"C!0B% +MP'4=H6"F!0B+4!0[4!@/@_0" #& @K_0!3_!2BG!0B+'Z3+\___'!"3 +MIP4(N! ")1"0$Z%;<__^+% +MA BU4(B1"+%?2F!0B)4 2C]*8%"(GL7<.-M"8 58GE5U93@^P,BQWT +MI@4(BWT(A=MT+HUW$XVT)@ ")="0$N 0 ")1"0(BP.)!"3H@=C__S'2 +MA56,?93@^P0.S60I@4(?3#LUD*8%"'S7,<"CD*8%"+@$ HYRF!0B#Q!!;7EW#B00DZ"_:___KTI"- +M="8 58GE5U93@>PL P BPV0I@4(.PV,I@4(BUT,#X1%!0 B #B50&9#')B?>)3 9H,=*)5 9LBT40A< /A; "A%*<%"(7 +M#X6C H1BG!0B%P'0:A=L/A)( "#^PH/A(D "#^PAT:XUT)@"+%9"F +M!0@QR8F-\/S__XG0P> $*=#!X ,QTHE,$*7UW#@SW,I@4( 700H="F!0B%P'2'C;0F (M%"( X+W0,BU44 +M@#H #X4,! BWT(H>"F!0B#^ -R$H/X! ^&; , (/X!0^$.0, (E\) 2+ +M%9"F!0C'!"0# B=#!X 0IT(U$Q@2)1"0(Z%#8__^)PX7;#XC( @ BPV@ +MI@4(A $* $*BQ60I@4(OP$ ")T,'@ +M!"G0P> #B4P&:(E\!FR0A=MT"(D<).B,UO__BPV0I@4(BS6(I@4(Z?']__^+ +M39CKP(D\)(L5D*8%"(G0P> $*="+%8BF!0B-1,($B40D!.@++0 BPV0I@4( +MBS6(I@4(B#J+#9"F!0B+-8BF!0B)R,'@ +M!"G(BT3&%"4 \ /0! /E< /ML"%P ^$4OS__XLUB*8%".D@_/__Z!#2 +M__^#. (/E,#KW8M-"(D,).C-TO__B<.+112)!"3HP-+__XU$&!&+50B#X/"+ +M310IQ(U\)"")5"0(B4PD!(D\).B5)0 BS6(I@4(Z;'[__^-! G!X04IP:., +MI@4(C03- (E$) 2AB*8%"(D$).AE?@ HXBF!0B+#9"F!0CIAOK__Y"- +M="8 58GE@^P8B5WXBUT(B77\B1PDZ)E^ "+50R%P(E"9'0/BUWXBW7\B>Q= +MPY"-="8 B1PDNP%>!0CHQ=Z?Q] "-M@ +M "-OP !5B>6#[ B+10B)!"3HWU< #')@#@N= B)[(G(7<.)]@^V4 &$ +MTG08@/HN=>N > ( =>6-M@ "-O"< N0$ #KT8GVC;PG %6) +MY5=64X/L'(M%"( X '0.BQ6$I@4(A=(/A5\! "AD*8%"(G#2W@ZB=C!X 0I +MV(TTQ0 ")]HV\)P "+%8BF!0B+1#)P@_@$#X1] @_@>='B#[GA+ +M>>*AD*8%",=%\ QVSG#?4_\BQ6(I@4(QT7L ,=%Z "-="8 +MC;PG (M-[(-\"G >=!S_1?")SHM]Z(-%Z'@!UKD> =?SI:&0I@4( +M@T7L>$,YPWS1BT7PHY"F!0B#Q!Q;7E]=PY"+10R%P ^%A0 (L$,H X+W0( +MBTT(@#D =36+1#)DB40D!(L$,HD$).CR[___BQ6(I@4(@WPR6#[!B)7?B+70R)=?R+=0B+2U2+5E0YT7P? +MN $ !_&(M+6+C_____BU98.=%\"0^?P ^VP(UV (7 = R+7?B+=?R)[%W# +MB?:+ XE$) 2+!HD$).AP____Z^*-M"8 C;PG %6X_____XGE@^P8 +MB5WXBUT,B77\BW4(BTM4BU94.=%\'[@! ?QB+2UBX_____XM66#G1? D/ +MG\ /ML"-=@"%P'0,BUWXBW7\B>Q=PXGVBP.)1"0$BP:)!"3HR,S__^OBC;0F +M (V\)P !5B>6+50R+10B)50B)10Q=Z0O___^-="8 C;PG %6) +MY8M5#(M%"(E5"(E%#%WI6____XUT)@"-O"< 5;C_____B>6#[!B)7?B+ +M70R)=?R+=0B+2TR+5DPYT7P?N $ !_&(M+4+C_____BU90.=%\"0^?P ^V +MP(UV (7 = R+7?B+=?R)[%W#B?:+ XE$) 2+!HD$).A0_O__Z^*-M"8 +MC;PG %6X_____XGE@^P8B5WXBUT,B77\BW4(BTM,BU9,.=%\'[@! +M?QB+2U"X_____XM64#G1? D/G\ /ML"-=@"%P'0,BUWXBW7\B>Q=PXGVBP.) +M1"0$BP:)!"3HJ,O__^OBC;0F (V\)P !5B>6+50R+10B)50B)10Q= +MZ0O___^-="8 C;PG %6)Y8M5#(M%"(E5"(E%#%WI6____XUT)@"-O"< +M 5;C_____B>6#[!B)7?B+70R)=?R+=0B+2T2+5D0YT7P?N $ !_&(M+ +M2+C_____BU9(.=%\"0^?P ^VP(UV (7 = R+7?B+=?R)[%W#B?:+ XE$) 2+ +M!HD$).@P_?__Z^*-M"8 C;PG %6X_____XGE@^P8B5WXBUT,B77\ +MBW4(BTM$BU9$.=%\'[@! ?QB+2TBX_____XM62#G1? D/G\ /ML"-=@"% +MP'0,BUWXBW7\B>Q=PXGVBP.)1"0$BP:)!"3HB,K__^OBC;0F (V\)P +M !5B>6+50R+10B)50B)10Q=Z0O___^-="8 C;PG %6)Y8M5#(M%"(E5 +M"(E%#%WI6____XUT)@"-O"< 58GE@^P8B7W\BT4(BWT,B5WTB77XBW+50B)1"0$BP*)!"3H#/S__^O>N/_____KTXUV %6) +MY8/L&(E]_(M%"(M]#(E=](EU^(MW-(M(-(M?,(M0,#G.?$%_!#G3Q=PXL'BU4(B40D!(L" +MB00DZ&3)___KWKC_____Z].-=@!5B>6+50R+10B)50B)10Q=Z0O___^-="8 +MC;PG %6)Y8M5#(M%"(E5"(E%#%WI6____XUT)@"-O"< 58GEBT4, +MBP")10R+10B+ (E%"%WIS\K__XVT)@ !5B>6+50R+10B)50B)10Q=Z\Z- +MM"8 C;PG %6)Y8/L"(M%#(L B40D!(M%"(L B00DZ/3Z__^)[%W# +M58GE@^P(BT4,BP")1"0$BT4(BP")!"3HG,C__XGL7<-5B>6+50R+10B)50B) +M10Q=ZZZ-M"8 C;PG %6)Y8M5#(M%"(E5"(E%#%WKKHVT)@ "- +MO"< 58GE@^P8B7W\BWT(B5WTNRX ")=?B+=0R)7"0$BP>)!"3HP,K_ +M_[DN B<.)3"0$BP:)!"3HJ\K__X7 =#^)1"0$A=N)V'0KB00DZ#WZ__^% +MP'0-BUWTBW7XBWW\B>Q=PXL&B40D!(L'B00DZ!SZ___KX;A%7 4(Z\Z-=@"X +M15P%".NZB?:-O"< 5;@N B>6#[!B)??R+?0B)7?2)=?B+=0R)1"0$ +MBP>)!"3H,,K__XG#N"X ")1"0$BP:)!"3H&\K__X7 =#^)1"0$A=N)V'0K +MB00DZ'7'__^%P'0-BUWTBW7XBWW\B>Q=PXL&B40D!(L'B00DZ%3'___KX;A% +M7 4(Z\Z-=@"X15P%".NZB?:-O"< 58GEBU4,BT4(B54(B44,7>G+_O__ +MC70F (V\)P !5B>6+50R+10B)50B)10Q=Z3O___^-="8 C;PG %6) +MY8/L*(E=]*&HI@4(B77XB7W\@_@%=V#_)(78:04(H:2F!0B#^ $/A'D! "# +M^ $/@E,! "#^ )U/:&LI@4(A< /A# (E$) BAD*8%"(E$) 2AB*8%"(D$).CO +MQ___BUWTBW7XBWW\B>Q=P[CPRP0(Z\.-=@"#^ -T'(/X!'6?H:RF!0B%P'0' +MN'#+! CKIKC@R@0(ZY^AI*8%"(/X 71*@_@!#^ (/A7#___^+%:RF!0B% +MTG0*N%#*! CI<____[C R00(Z6G___^+#:RF!0B%R70*N##)! CI5?___[B@ +MR 0(Z4O___^+':RF!0B%VW0*N!#(! CI-____[B QP0(Z2W___^AK*8%"(7 +M= JXD,T$".D:____N.#,! CI$/___[A0R00(Z<;^__^AK*8%"(7 = JX$,D$ +M".FS_O__N##(! CIJ?[__Z&LI@4(A?[__[C0RP0(Z6_^__^AK*8%"(7 = JX<,T$".E< +M_O__N%#,! CI4O[__XGVBSVLI@4(A?]T"KA0RP0(Z3S^__^X<,H$".DR_O__ +MB?:+-:RF!0B%]G0*N+#+! CI'/[__[B0RP0(Z1+^__^)]E6)Y8/L&(E=^(EU +M_*&@I@4(@_@$=TK_)(7P:04(,=L['9"F!0A].3'VC;0F *&(I@4( ?") +M!"3H(0T *%@I@4(BU 4.U 8DT$0 +MZ(\8 "+7?B+=?R)[%WI@1, (M=^(MU_(GL7>D#%@ C78 58GE@>RX +MB5WTH22C!0B)=?B)??R%P'@-BUWTBW7XBWW\B>Q=PS' C9UX____OF0 ") +MA73___^-A73___^)!"3H/\/__XF%6-1?B#[!B) +M1"0$QP0D .C_QO__A!0B)5"0$B1PDZ''&__^)70B+=?R+ +M7?B)[%WI8,/__XET) BX)%X%"(E$) 3KV(DT).A16@ Z[WK#9"0D)"0D)"0 +MD)"0D)!5NH@4 ")Y5>-A4CK__]64X'LO!P (U=V(F5;./__XM5"(E<) 2) +MA7#C__^+0A2)!"3H^$L (M-"(!Y= /A-0% "P*XA%XJ&DI@4(QD7C (/X +M 0^$HP4 (/X 0^"CP4 (/X @^$; 4 (GVH=RF!0B+M7#C__^%P ^%^00 +M *&XI@4(A< /A74$ "+50BY*UX%"(M"&(E,) 2)-"2)1"0,B5PD".B)Q?__ +MB30DZ('"__^+%8BB!0@!QH72#X4E! H8RB!0B%P'0VH;2F!0B%P ^$_ , +M #' A< /A-X# ")1"0(N!Y>!0B)1"0$B30DZ#S%__^)-"3H-,+__P'&@#VP +MI@4( ^%F0, (M-"(M!%"4 \ /0 @ /A$\# ] & ^$1 , (M% +M"#'_BQ64H@4(BU@TBT@PH9"B!0B)7"0$NS1>!0B)1"08N $ ")1"00H;RF +M!0B)5"0./__XE$) B+ +MA73C__^)P8D$),'Y'XE,) 3HUU@ (G#Z.#Y__^)1"0(N#Y>!0B)-"2)7"0, +MB40D!.C/P?__B30DZ,>^__\!QND>_O__BU4(#[9")(E$) R+0B2+4BB)-"0/ +MK- ()?\ ")1"0(N$->!0B)1"0$Z)'!___IY_S__XM5"(M"'(DT)(E$) 3H +MTOK__P'&Z4[\__^+30B+02")1"0(N"1>!0CI%_S__XM5"(M"((D$).C)5@ +MZ?/[__^+30B+01R)-"2)1"0$Z)+Z__\!QNG"^___H<"F!0B_/EX%"(L5Q*8% +M"(M-"(E$)!BX ( (E$)! QP(E$)!2AO*8%"(E4)!R)1"0,C84HYO__B40D +M"(M!/(M10(D$)(E4) 3HO$D (E$) RAG*8%"(DT)(E\) 2)1"0(Z,O __^) +M-"3HP[W__P'&Z13[__^+30B-A;CH__^)1"0(BU%@BT%Q,( NP @ ")7"0$BWT,C9WHW___B40D#(MU$(D<)(E\ +M) B)="00Z$9B ")A=C?__\]_Q\ (F=U-___P^'9@( (L]^*8%"(7_#X71 +M BT44A3 B[74W___BX78W___B?#?__^)E>3?__^-=@")?"0$ +MC87@W___C97(/X +M_G1EA#J%VW05C70F (V\)P +M /M@='B 9&2W7V 970W___C87@W___B00DZ'J]__^%P ^$>O___^E'____ +MQ@8_ =]&_X70W___Z]>+OD1____B[74 +MW___BX78W___B?7<.#Q!!;7EWK4(D<)+@$ B40D!.B7N___BU,,Z[2)'"2X! (E$ +M) 3H@;O__XM3#.D_____D(E$) B)5"0$B30DZ$ " #I#?___XUT)@"-O"< +M 58GE@^P(H;2B!0B%P'01QP0DL*(%".B5 P B>Q=PY#'!"2@H@4(Z(0# +M #'!"2XH@4(Z'@# #'!"2HH@4(Z]7K#9"0D)"0D)"0D)"0D)!5B>6![+@" +M ")7?RAW*8%"(M="(7 #X74 H;BF!0B%P'5@,Q=PXGVBT,4B00DZ+4 #KZHUV (M#%.O+H<"F!0B+%<2F +M!0B)1"08N " ")1"00,<")1"04H;RF!0B)5"0!0CH@[C__^GR_O__C;8 58GE@^P(BTT(B('Z $ +@O =#J!^@"@ "X0 '0M@?H $ N'P !T +M(('Z , '0,D(VT)@ ")[%W#N#T "-M"8 BQ5@I@4(BTH4.TH8 +M57OP0 !64X/L'(M5$(M%"(M- +M#(/Z_XE%\ ^$/@$ (G()0#P ] $ ^$(@$ #T H #X3V /0 0 +M /A.$ ] , ^$S #T 8 #X2W /0 @ /A*( #VP4ET +M#[\- C78 C;PG #';@_\$=#G'!"2@H@4(Z.T "%VXT$_:"B!0AT +M XU#"(D$).C7 QT4(J*(%"(/$'%M>7UWIQ (UT)@"+1?")!"3H#;;_ +M_P%%\(G&BQV I@4(A=MTK8VV (L3.?)W'(M%\(E4) @IT(M3!(D$)(E4 +M) 3H:K;__X7 =(6+6Q"%VW77Z7G___^_"@ .EH____OPD #I7O___[\( +M Z53___^_!P .E*____A=)U$Z$$HP4(A564X/L$(M%"(L8BW $A=M^*(VV (V_ ^^!D:+%6"F!0B+ +M2A0[2AAS$8@!_T(42X7;?^2#Q!!;7EW#B10D#[; B40D!.A&L___Z^.-M@ +M "-OP !5,<")Y8/L&(M5"(E=_(L:A=M^'XE<) B+0@3'!"0! B40D +M!.B5/@ .=@/E< /ML"+7?R)[%W#C;8 58GE@^PHB5WXH=RF!0@QVXEU +M_(7 BW4(= 6[" (L-N*8%"(7)= V+%9RF!0B)V '0C5@!C47TB40D#*'\ +MI@4(B40D"(L&QP0D (E$) 3H2/?__XM%](L-S*8%" '#A( \ @?H @ =#F!^@! !T+H'Z * '0F@?H $ =!Z!^@# !T +M%HVV (G8BW7\BUWXB>Q=PXUT)@!#Z^U)=>JH273FZ_2-="8 58GE5U93 +M@^P\H22G!0B+%9"F!0B)1=PYT(E5U'X#B57R+1=2#1 0 BPT@IP4(C012C42!](E-T(L(A#!X 0IT(E%S,=%V "+3>@Q_XMUQ(E-[*&(I@4(C1SU +M '8B00DZ)KY__^AB*8%" '#B1PDZ(O]__^)1>2+5?"+3=B+0@B+5> !5>R+ +M!(A!B4W8BTW, P[#9"F!0A]&8M5Y(T<.(E<) 2-!#J)WXD$).@[! +MZYFA8*8%"(M0%#M0&',AQ@(*_T 4_T7HBTW@@T7$#SE-Z ^,8?___X/$/%M> +M7UW#B00DN@H ")5"0$Z *P___KTJ$@IP4(B470Z?3^__^-<0'IH_[__XL- +MD*8%"(E-U.F@_O__D(UT)@!5B>575E.#["RA)*<%"(E%X*&0I@4(.47@?@.) +M1>#'1>P .47L#XWB QT74 (UT)@"-O"< BQ6(I@4(BT74 +M =")!"3HC?S__\=%T "+5> Y5=")1>@/C9$ "A(*<%"(E%V.L-D)"0 +MD)"0D)"0D)"0D(M5T(T$4HT YQGR&_T7L +M@T74>(M5[#L5D*8%" ^,,/___XMUX(/^ 0^.4@$ *$@IP4(B478BU78C01V +MC42"](GVBSB%_W4)3H/H#(/^ 7_QQT7D (M5V(T$=HU$@O2)1?"AB*8% +M"(D$).B ]___H8BF!0B)!"3H<_O__XE%Z(M5\,=%[ $ "+0@B+"+@! +M.P60I@4(?7V_> .L-D)"0D)"0D)"0D)"0D(M%[)GW_H72B57<#X6< +MH6"F!0B+4!0[4!AS><8""O] %,=%Y "AB*8%" 'XB00DZ ;W__^AB*8% +M" 'X@\=XB00DZ/3Z__^)1>B+5?#_1>R+0@B+5=R+#)"+1>P[!9"F!0A\EZ%@ +MI@4(BU 4.U 8Q^0:%@I@4(BU 4.U 8-O\O= O& 2]!C;0F ^V X3 =!2)]HV\ +M)P "( 4-!#[8#A,!U]<8! %M>7<. ?@$ =-CKK8VV %6Z5E5558GE +M5U93@^PB+'2"G!0B#P .)1#L$BT7PQP0[ 0 (7 =3HQ +MTCGR?QB+'2"G!0B+1#L(B?;'!) # 0CGR?O2#1>@#1H/'#(-%[ 0[-22G +M!0A\L(/$'%M>7UW#BT7LB00DZ)U7 ")1#L(Z[6A)*<%"(T$0,'@ HD$).B$ +M5P HR"G!0C'1? ! Z4K___^-=@!5B>53@^P4BUT(A=MT0L<$) "X +M!0 (E$) BXH&T%"(E$) 3H/*K__XE$) 2+%>RI!0BA9*8%"(E4) B)!"3H +M :K__XD<).A)K?__D,<$) "X!0 (E$) BXX&T%"(E$) 3H^JG__XD$ +M)(L5[*D%"(E4) 3H"*S__\<$) "Y(&X%"+@% B4PD!(E$) CHRJG_ +M_XD$)(L58*8%"(E4) 3H>*G__\<$) "Z!0 +C ;@4(B50D"(E$) 3H +MFJG__XD$)(L58*8%"(E4) 3H2*G__\<$) "X!0 (E$) BX(&\%"(E$ +M) 3H:JG__XD$)(L58*8%"(E4) 3H&*G__\<$) "X!0 (E$) BX0' % +M"(E$) 3H.JG__XD$)(L58*8%"(E4) 3HZ*C__\<$) "YX'$%"+@% +MB4PD!(E$) CH"JG__XD$)(L58*8%"(E4) 3HN*C__\<$) "Z!0 +B@ +MJC__XD$)(L58*8%"(E4) 3H**C__\<$) +M "YH'@%"+@% B4PD!(E$) CH2JC__XD$)(L58*8%"(E4) 3H^*?__\<$ +M) "Z!0 +@@>@4(B50D"(E$) 3H&JC__XD$)(L58*8%"(E4) 3HR*?_ +M_\<$) "X!0 (E$) BX@'L%"(E$) 3HZJ?__XD$)(L58*8%"(E4) 3H +MF*?__\<$) "X!0 (E$) BX0'T%"(E$) 3HNJ?__XD$)(L58*8%"(E4 +M) 3H:*?__\<$) "Y 'X%"+@% B4PD!(E$) CHBJ?__XD$)(L58*8% +M"(E4) 3H.*?__\<$) "Z!0 +C@?P4(B50D"(E$) 3H6J?__XD$)(L5 +M8*8%"(E4) 3H"*?__\<$) "X!0 (E$) BX8((%"(E$) 3H*J?__XD$ +M)(L58*8%"(E4) 3HV*;__\<$) "X!0 (E$) BXP(,%"(E$) 3H^J;_ +M_XD$)(L58*8%"(E4) 3HJ*;__\<$) "YX(0%"+@% B4PD!(E$) CH +MRJ;__XD$)(L58*8%"(E4) 3H>*;__\<$) "Z!0 +@@A04(B50D"(E$ +M) 3HFJ;__XD$)(L58*8%"(E4) 3H2*;__\<$) "X!0 (E$) BX8(4% +M"(E$) 3H:J;__XD$)(L58*8%"(E4) 3H&*;__\<$) "X!0 (E$) BX +M (8%"(E$) 3H.J;__XD$)(L58*8%"(E4) 3HZ*7__\<$) "X!0 +E/ +M7@4(B40D"(E,) 3H"J;__XD$)+IF7@4(B50D!.@9J/__Z=/[__^0D)"0D)"0 +MD)"0D)!5,=*)Y8/L"(M%#(M $"4 \ /0"@ !T$8M%"(D$).BSIO__A<") +MPG@&B>R)T%W#Z!*F__^+ (/X)G0*@_A?NO____]UY#'2Z^"0C70F %6)Y5=6 +M4X/L'(M%#(M="(MU$(E%\+@ @ B40D!(D<).@BJ?__A<")QP^$$ ( (E$ +M) BX ( (E$) 2+1?")!"3H *C__X7 #X3 Z*.E__^)1>B+ (/X)HE% +M['1Q@_A?=&R)/"3HR:7__XET) 2+1?")!"3HJJ7__XM%\(D$).CG0P QP0D +M (G#N 4 ")1"0(N#.'!0B)1"0$Z.^D__^)1"0(B5PD#(M%[(E$) 3' +M!"0 Z!2I__^Z_____X/$'(G06UY?7<.)/"3H[:/__XD\)(G#Z%.E__^# +M^P-UA8ET) 2+1?")!"3H+Z7__S'2A+ (/X)G0% +M@_A?=16)=0R+1?")10B#Q!Q;7E]=Z2@ ")'"3H $( (E$) RX3H<%"(E$ +M) B+!^DP_O__C;8 C;\ 58GE5U93@^Q,H5&'!0B+70R)1/;#('0$QD72/;#!'0$QD79(U%R(D$).A_I?__A<")Q@^$ +M5 $ (E$) BX ( (E$) 2+10B)!"3H_:3__X7 #X21 Z*"B__^)QXL +MB30DB47$Z-&B__^+!X/X7W1:@_@F=%6+10B)!"3H\D ,<$) ")P[@% +M B40D"+AFAP4(B40D!.CZH?__B40D"(M%Q(E<) R)1"0$QP0D .@? +MIO__NO____^#Q$R)T%M>7UW#B5PD!(M%"(D$).A1HO__,=*%P'3ABS^)?<3K +MCXDT).A,HO__]L<.=6*!XP#P "!^P! !T!#'2Z[N+10B)!"3H:J7__X7 +M=.V+10B)!"3H4T +D% NF:'!0B)QHE,) B)5"0$QP0D .A;H?__ +MB575E.#[ R+71")V$N%P'4-BT4(@\0,6UY?7<.)]NBCI/__B<>)]HV\ +M)P "+10P/MA0#BP575E.![.P$ "+112)C9#[ +M__^+50B+312+0 B)E8S[__^+22@QTHF%F/O__X/X#(F5A/O__XF-E/O__P^. +MZA8 (/H#(F%F/O__XM%$(F%B/O__P^V (3 #X2M B,$QP#';,?:)A8#[ +M__^______P^^P8F=:/O__S';@_@_B;UP^___B;5L^___#X]Z%@ @_@F?26# +M^",/C_@! "#^"!]%X/H"(/X!0^'BP (UT)@"-O"< BY60^___BT4, +M*= QTH/X 79>B[V,^___A?]T#XN%C/O__X@(0(F%C/O___^%D/O___^%B/O_ +M_XN5B/O__P^V H3 B,$/A5O___^-M@ "+E8S[__^%TG00BT4,A@?2"'PC3P8BT4,*= QTCG'#X,X____BX6,^___AA__\!M8S[__^)7"0(BY6(^___ +MBXV,^___B50D!(D,).CUG___ 9V,^___ ;V0^___BX6(^___C5P#_XF=B/O_ +M_^F-_O__0^EJ____B30DZ,:>__\!P^E;____BY6(^___B10DZ+&>__^+C8C[ +M__^-1 @!B86$^___Z#^C /CR$4 "#^B,/A X4 "#^BUU"P^^P(F% +M@/O__^O%#[[ @^@P@_@)=U@QP(F% $E +M=?&+G7#[__^+E9#[__\I\XG8P>@?2"'8C3PPBT4,*= QTCG'#X.Y_/__BX6, +M^___A +M__\!G8S[__^+C6C[__^%R70PB70D"(N%B/O__RGP0(E$) 2+E8S[__^)%"3H +MN/K__P&UC/O__P&]D/O__^D(_/__B70D"(N%B/O__XN-C/O__RGP0(E$) 2) +M#"3H,)W__^O.B5PD"(N-C/O__[@P B40D!(D,).N"_XV(^___Z0/___^+ +MO7S[__^%_P^%]?[__X7;=!.^ 0 #';B;5H^___B9UL^___QH6<^___)8N= +M?/O__XV%G?O__X7;=!,/MHU\^___C86>^___B(V=^___#[:59/O__XV]V/O_ +M_\9 0"($(M-%(V%G/O__XE$) B)/"2)3"0,N0 $ ")3"0$Z!>;__^%P(G& +M=0V O=C[__\ #X7Z BYUP^___BY60^___*?.)V,'H'T@AV(T$,(F%7/O_ +M_XM%#"G0,=(YA5S[__\/@SK[__^+A8S[__^%P'1BA=M^+H.]@/O__S /A)( +M ")7"0(BY6,^___N" ")1"0$B10DZ'R=__\!G8S[__^+A6S[__^%P'5/ +MBX5H^___A_?__@[U\^___10^$<_[__XM%%(N]/O__XUUY'0"]]NYSH#C022 < IPXC8!#")TX72B 9UY8N->/O__X7)= 1.Q@8M@[V ^___+0^$ +MY@$ (U5V(GP*=")E4#[__^-7#CTA=L/CH8 "#O8#[__]?#X19 0 BY60 +M^___BT4,*= QTCG'#X-Z^?__BX5X^___A " +M*854^___B[54^___@\9DB?")\??KB94L^___BYTL^___B84H^___B?"9P?L% +M*=.-!)N-!(#!X +IK/O__[\" ZXZ+O7#[__^#_P%\"XN-5/O__^G]]?__ +MOP$ #K[H'':P< #' B;U4^___]H54^___ XF%2/O__W5AB?B^'X7K4??N +MBX54^___B=?!^@6)E33[__^9*94T^___BX4T^___C02 C02 B84H^___P> " +M.854^___=1B)^,'X!RG0C02 C02 P> $.854^___=0N^ 0 (FU2/O__XN5 +M2/O__XV<&FT! "+E4S[__^)V"G0C;!^ 0 N),D29+W[HGPP?@?C10RP?H" +M*<*-!-4 *= IQBGS@\,#B9U0^___Z4[^__^%VW03N $ ")A6C[__\Q +MP(F%;/O__XN%?/O__X7 #X0Z\___Z1/R__^#O7S[__]%#X0&\O__B[UP^___ +M@_\#?06_ P (M%%(M('$'IV?3__X.]?/O__T4/A-WQ__^+O7#[__^#_P)\ +M"XM5%(M*".EW_/__OP( #K[H.]?/O__T4/A+/Q__^+O7#[__^#_P)\"XN- +MF/O__^E-_/__OP( #K[HVV (.]?/O__T4/A(/Q__^+O7#[__^#_P)\ +M"XM%%(M($.E]____OP( #K[HVV (N=@? +M2"'8C7 !BT4,*?@YQ@^#.>[__XN-C/O__X7)#X0$\?__A=M^*H.]@/O__S!T +M-HE<) B+C8S[__^X( (E$) 2)#"3H>Y#__P&=C/O__XN%C/O__\8 "D") +MA8S[___IP?#__XE<) BZ, (E4) 2+E8S[__^)%"3KR/RX"P (MU%(G! +MC;VH^___C86H^___\Z6)!"2[9V9F9HUUY.C'2 B<'!Z!^)A7C[__^)R(N] +M>/O___?KBW_ +M_XN%C/O__X7 #X0(\/__A=M^*H.]@/O__S!T+XE<) BY( (E,) 2+C8S[ +M__^)#"3H?X___P&=C/O__XN%C/O__\8 ">G__O__B5PD"(N5C/O__[\P +MB7PD!(D4).O/B[UP^___@_\!?#"+512XDR1)DHM:&(/#!O?KB=C!^!^-%!K! +M^@(IPHT$U0 IT"G#C4L!Z8_R__^_ 0 .O)@[U\^___10^$C.___XN] +M "*<;IQ/O__[\" Z\*+512+2B"%R0^(S.O__XMZ)(7_#XCJ +M BYUP^___BY60^___2XG8P>@?2"'8C7 !BT4,*= QTCG&#X/9Z___BX6, +M^___A $*="-')N-%(4 B? IT(T,F.D@\?__B5PD"(N-C/O__[@P +MB40D!(D,).EN____BYUP^___BY60^___2XG8P>@?2"'8C7 !BT4,*= QTCG& +M#X/OZO__BX6,^___AO__X/Z7G0(@_I?Z=[K__^X 0 (F%:/O__^F@Z___@_A!#XPGZO__@_A? +M#XZ>Z?__@^AA@_@=Z8+I__^-=@"+A9C[__^%P ^%$>G__[@, Z0'I__^0 +MD)"0D)"0D%6)Y8M%"%V+0 C#D(UT)@!5B>6+10A=BT ,PY"-="8 58GEBT4( +M78M $,.0C70F %6)Y8M%"%8Q]E.+"(M8!#G9\8D$)-U< +M) SH X?__XE<) BXRHD%"(E$) 2+10R)!"3HZX;__X/$'%M>7UW#C70F (V\ +M)P !5B>575E.#[ R+=0B+?0R+1@B)/"2)1"0$_U88BQ:-!,([1@1S/XL0 +MA=)T*X7 B<-T)8VT)@ "-O"< BP.)/"2)1"0$_U8DQP(/$#%M>7UW#BP/K].ANA___C;8 58GE@^P(BT4(BT@0A57 +M5E.#[ 2+10B+?1"+&(M !(E%\#G#R#PP@[7?!RU(UT)@!;B%VW7EBU4(BT($@\8(.<9RR)"- +MM"8 @\0,B?A;7E]=PXVV %6)Y8M-"%:+=0Q3,=L/MA&$TG0HC;8 +M C;\ B=@/MM+!X 4IV$$!T#'2]_8/M@&$P(G3B,)UY(G86UY=PXVT +M)@ "-O"< 5;D# B>57BWT(5KX) .?Y37UV%T@^5P,.-M"8 C;PG %6)Y5.#[ 2+70B# +M^PEW!;L* @\L!B?:-O"< B1PDZ&C___^$P'4%@\,"Z^]9B=A;7<.) +M]HV\)P !5H8B)!0B)Y8M5"(D"H8R)!0B)0@2AD(D%"(E""*&4B04(B4(, +MH9B)!0B)0A!=PY!5V>Z)Y8M5"(M*%-E!"-G W>+?X)YV>=GHW>'?X-W9GG9N +MV4$,W>G?X)YV9-D!V<#=Y-_@W=R>=CC9 +MR]WKW^#=VIYR)]KIW^">=A*X 0 (UT)@"-O"< 77UW#B1PDZ'N%__^-=@ QP.OIB?CI +M;____[Z(B04(Z2#___^-="8 C;PG %6)Y5=64X/L#(M]"(LWBT<$.<9S +M4HUT)@"-O"< BQ:%TG0ZBUX$A=MT98M/((7)=5+' P "+4P2+1R2% +MTHE#!(E?)(G3=>2%R74MQT8$ (M'!,<& (/&"#G&7UW#BP:)!"3_T>O*BP.)!"3_T8M/(.NBBT\@Z[6)]HV\ +M)P !5B>575E.#[ R+10B)1?"+<""%]@^$Q@ (M8$(7;#X2[ BSB+ +M0 0YQW-PBP^%R70DA?^)^W0>C;8 BP.)!"2+3?#_42"+6P2%VW7NBU7P +MBT($@\<(.<=RSXM-\(L1.<*)UW,TC;8 BU\$A=MT((GVC;PG (MS +M!(D<).@MA/__A?:)\W7OBU7PBT($@\<(.<=RTHM-\(M9)(7;=!F0C;0F +M (MS!(D<).C]@___A?:)\W7OBU7PBP*)!"3HZH/__XM-\(E-"(/$#%M>7UWI +MV(/__XM%\(L0BT $Z7?___^-=@!5B>6#[ B+30B+022%P'00B<*+0 2)022) +M[(G07<.)]L<$) @ #H[(#__XG"Z^A5B>6+10B+50R+2"3' @ ")2@2) +M4"1=PXVV %6)Y5=64X/L#(MU" ^V112+?0R(1?.+1@B)/"2)1"0$_U88 +MBQ:-',([7@0/@Z0 "+11"+$XD8,<"%TG0TB50D!(D\)/]6'(3 =$: ??, +MBSMT'(M+!(7)=1_' P #K#9"0D)"0D)"0D)"0D)")^(/$#%M>7UW#BP&+ +M402) XE3!(E,) 2)-"3H4/___^O>BT,$A +M@/__C;8 58GE5U93@^PLBU4(BT(@B40D$(M"'(E$) R+0AB)1"0(BT(4 +MB40D!(M%#(D$).C=^___B<!__^+50B+!XD" +MBT<$B4($BT<(B4((BT<,B4(,BT7UW# +MB3PDZ,/]__^)PC' A=)TZ(M&!(M-Z(E"!(D*B58$ZXG_1PR+1>@[7?")!@^$ +M>/___XE<) 2)/"3HOOW__^EG____Z,Q^__^-="8 58GE@^PHB5WXBUT,B77\ +MBW4(A=L/A!D! ")7"0$,O?X-W:GG9&@'D0 '0YW=G8 +M20S9??(/MT7RB30D9@T #&:)1?#9;?#??>C9;?*+1>B)1"0$Z,3]__^$P ^4 +MP ^VP$@AP^N&V$D,WLGKQ=W8W=CI=O___XDT).AO_/__B<$QP(7)#X0:____ +M_T80BU7TB1F+0@2)002)2@3I3/___^B0??__D(VT)@ !5N $ ")Y8/L +M*(E$) R-1?2)7?B+70B)1"0(BT4,B77\B1PDB40D!.AB_/__B<8QP(7V=#?_ +M2Q"+1?2+ (7 =2G_2PPQTHM##%(QTE"+0PB+2Q3?+"2#Q A24-\L)(/$"-@) +MVNG?X)YW#(GPBUWXBW7\B>Q=PXD<).AA^/__BT,,,=)2,=)0BT,(BTL4WRPD +M@\0(4E#?+"2#Q C9 =C)W>K?X-W9GG8Z@'D0 '0LV$D$V7WR#[=%\HD<)&8- +M QFB47PV6WPWWWHV6WRBT7HB40D!.B7_/__ZY#8203820CKS]W8ZX20D)"0 +MD)"0D)!5B>6#[ C'!"0! Z#[1__^)[%W#C78 C;PG %6)Y5=64X/L +M'#';QT7P_____XM%",=%[ ")!"3HI'S__XE%Z(M5#(L"A< /A)T #' +M1>0 D(UT)@"+50B+1>B)5"0$BU4,B40D"(L$FHD$).C\?/__A'K__\<$) $ ")QHM%"(E$) 3H"QD ,<$) ")PXM%#(E$ +M) BX!0 (E$) 3H72, (E$) PQP(E<)!")="0(B40D!,<$) #H9W[_ +M_XM=^(MU_(GL7<.0C70F +H% N$&*!0B)5"0(ZX!5B>575E.#["R+10C' +M1> BU4,BTT0B47PN 4 ")5>R)3>B)1"0(N%R*!0B)1"0$QP0D +M .C(>?__B40D!*%DI@4(B00DZ)=Y___'1>0 BU7PBP*%P'1HQT78 +M (M-[(G#B4WR+1>@!]XMUX#G B<'SIG1RB5PD"*%D +MI@4(NW&*!0B)7"0$B00DZ$!Y__^+5=R)5>#_1>2+3>@!3=B+5>0!3=R+3?"+ +M!)&%P(G#=:>A9*8%"(M0%#M0&',.Q@(*_T 4@\0L6UY?7E^>/__C;8 B5PD"*%DI@4(N7N*!0B)3"0$B00DZ,YX___K +MDHUT)@!5B>6#["B)7?2+11"+70R)=?B+=1B)??R+?12)="0,B40D!(E\) B) +M'"3H(?W__X7 B<)X#XM=](G0BW7XBWW\B>Q=PXE$) B+10B)7"0$B00DZ.G] +M__^)="0(BT40B7PD!(D$).AV_O___U4R+1?"+!(*%P'7:,<"#Q A; +M7E]=PXM5\(M-#(L$D>OMD)"0D)!5B>6+30@/M@&)RH3 = V0/"]T#$(/M@*$ +MP'7T78G(PT(/M@(\+W3XA,!T!(G1Z^2 .2]UYXU*_^OBC;8 58GE@^P( +MB5W\BUT(B1PDZ-1X__^#^ %V!X!\&/\O= >+7?R)[%W#2(/X 7;S@'P8_R]U +M[.OQD)"0D)"0D)"0D%6)Y8M%"%VCI*@%",.-=@!5,<")Y8/L*(L58*8%"(E= +M^(EU_/8"( ^5P(G#2W02B10DZ&%V__^%P'1UBQ5@I@4(B10DZ&]Y__^%P'0' +MZ(9W__^+&(7;>%C'!"0 O@4 "Y@HH%"(ET) B)3"0$Z )W__^)QJ&D +MJ 4(AQ=PXVV (ET) RX3H<%"(E$) BA,*,%"(E< +M) 2)!"3HVGK__^O058GEBT4(BU4,]L0(= J >@-X=#+&0@-3]L0$= J >@9X +M=!W&0@93]L0"= J >@EX= ;&0@E47O__ +M_X@#]\8 0 L')U K MB$,!B?*$TK!W> *P+8A# O?&0 +!X=0*P+8A# +M _?&( +!R=0*P+8A#!/?&$ +!W=0*P+8A#!??&" +!X=0*P+8A# +M!O?&! +!R=0*P+8A#!_?& @ +!W=0*P+8A#"/?& 0 +!X=0*P+8A# +M"8E=#(EU"(/$$%M>7>F2_O__B?95B>6+10B+0!")10A=Z3[___^0D)"0D)"0 +MD)"0D)"0D%6)Y5@#A#=0: > $ =!'\OY6*!0BY!@ (G&\Z9U C';B=B+=?B+ +M7?2+??R)[%W#D)"0D)"0D)"0D%6)Y8/L2(E=](M="(EU^(E]_(/[ =MM#'1F +MVRW B@4(W>G?X)YV6=G VSPD,?8Q_]M]V.B(, B470A=N)5=3;;=AU6]]M +MT(M%U(7 >$;:Z=_@GGH"= B^ 0 #'_D(M%T(M5U 'P$?I24-\L)(/$"(72 +M>!>-=@"-O"< BUWTBW7XBWW\B>Q=P]@%X(H%".OKV 7@B@4(Z[*-=@#= +MV.N\C;8 C;\ 58GE5[______5E.#[%R+112+70B)!"3HK'3__XE% +MM(MU#(U5N(E<) 2)%"0!\XET) B-M"8 C;PG .B#=?__BU40#[8" +MA,!T##Q^B?=W P^V^/]%$#G^"+?1PQP(E5Y(/A +M XM5(/9%%"")3=2)5=@/E<#'1;!B7 4(QT6L 0 ,=%I$5) +M!@ <@DY==@/AWX& ")-"2+1=B+5=R)?"0$B40D"(E4) SH'S0 (G1"<%U +M>8DT)(M%V(M5W(E\) 2)1"0(B50D#.C/,@ B40D"(M=X(M%X(E4) SW9"0( +MB=&)10QT8M5X#'0"<$/A P# "-M@ "-OP #?;=B+1=R%P ^(YP( +M -]MX(M%Y(7 #XC. @ 5U;?+"2#Q B%_P^(LP( /9%%!#>\M[)V<#;O7C_ +M__\/A&D" #=V,=%P "+1= QTE+9Z%#?+"2#Q B-M@ #;K7C____9 +MRMC)_T7 V<#8RMG+W>O?X-W:GG(&@WW "'S>W=C;K7C___^^Y(H%"(M%U-[Q +MB00DV<#;O7C____;?"0$Z*C\__^)="0$BU40VWPD"(D4).@-=?__BTT0B0PD +MZ )R__^)PXG"BW6LBT6L1H/ O9%%"!U!HM%K(/ SG8#X)R 0 ]D44"'02 +MBT40@'P"_S /A%X! "-="8 B5PD"(M-N(M%$"G9B4V\B0PDB40D!.A-"*!0CI0OW__]@%X(H%".DG_?__V 7@B@4(Z0[]___'1<0 QT6T +M /9%%! /A%L! #'1< ,=*+1= [5

)="0(BTW,BU7(B7PD#(E,) 2)%"3H2C (ET) B)P8M5Q(E\) S! +MX0(!P8T$2C'2]W70B85T____BT6TT?B-'%"+1( +M@WW4 0^$O0$ (M%U(7 =2&+?;2%_WX:QT6T (N-=/___T&)3<2#^0H/ +MA($! "#?

OO@T7( <=%Q "#5

")3"0$B40D"(D4).C$*P B47(BT68B57,BU6Z 0 #')B>R)T(G*7<.-="8 58GE@^PHB5WTBUT(B77X +M,?:%VXE]_(M]# ^$[ ( [)P^$V (D<)+@$ N:B*!0B)1"0,N+2* +M!0B)1"0(B4PD!.A5[O__AQ=PXVT)@ ")?"0,N/"*!0B)1"00,<")1"0(C47P +MB40D!(D<).BN&P A)5P3I-?___U6)Y8/L* ^V10R)7?2+71")=?B+=0B(1>^- +M1?")??R)1"0(B5PD!(DT).BA_O__BU,$B<&+ XG7"<)!"3H>Q4 (G"B5,$H:BH!0B)0PB)T(D=J*@%"(/$#%M> +M7UW#BT,$Z_.)]HV\)P !5B>575E.#[ R+':BH!0B+?0B%VW0@#[8'B$7S +MC70F ^V5?.+0P0X$ ^$I0 (M;"(7;=>J+':RH!0B%VW0B#[8'B$7SC78 +MC;PG ^V5?.+0P0X$'1EBUL(A=MU[HD\).C^:?__QP0D# (G&Z-@3 +M ")/"2)P^C.% B4,$A?9T'XM&"(G:B0.AJ*@%"(E#"(D=J*@%"(/$#(G0 +M6UY?7<.AK*@%"#'2B4,(B1VLJ 4(Z^2-M@ ")?"0$B00DZ$QF__\QTH7 +M=8GKRHE\) 2)!"3H.&;__X7 B=H/A47____KLHUT)@!5B>575E.#[ R+';"H +M!0B+=0B%VW05C78 C;PG #DS=$Z+6PB%VW7UB30DZ)5F___'!"0, +MB(1?.-="8 #[95\XM#!#@0#X2E BUL(A=MUZHL=M*@%"(7;="(/ +MM@>(1?.-=@"-O"< #[95\XM#!#@0=&6+6PB%VW7NB3PDZ'YD___'!"0, +M B<;H:!( (D\)(G#Z%X3 ")0P2%]G0?BT8(B=J) Z&PJ 4(B4,(B1VP +MJ 4(@\0,B=!;7E]=PZ&TJ 4(,=*)0PB)';2H!0CKY(VV (E\) 2)!"3H +MW&3__S'2AO*B7PD!(D$).C(9/__A<")V@^%1?___^NRD)"0D%6)Y5=6 +M4X/L'(MU#(M%$(M="(7VQD 5 (UX%7A3C78 B1PD,#HW6;__X/X 0^&)P$ +M #MUX'-'C;0F ^^!H/X7P^/! $ (/X07TA@_@@?#J#^"-^%X/H)8/X +M&NL-D)"0D)"0D)"0D)"0D'<>1D<[=>!RQXVT)@ ")^(/$+%M>7UW#C;8 +M QT7H ,=%[ ")]HET) 2-1>B)1"0,BT7@*?")1"0(C47DB00D +MZ-ED__^#^/^)PW1R@_C^=%:%P'4%NP$ "+1>2)!"3H.6?__X7 >!T!QXU% +MZ '>B00DZ.9F__^%P'2JZ7G___^0C70F /9%$ *X_____P^%<____XM%Y(D$ +M).B>9/__A /@QS____H[V;__XG!D(UT +M)@ /M@9&#[;0BP$/MQ10]L9 = Y'.W7@K__XE%\(D<).A>8___BU7PB<:-1 ("B00DZ,UB__^)QS' A?]T6(E< +M) 2)="0(B3PDZ,5E__^%]HG#=!> >/\O=$2+50R .B]T"<8 +T.0C70F (M% +M$(7 = 6+11")&(D<)(M5\(M%#(E4) B)1"0$Z/9C__^+5?#&!!H B?B#Q!Q; +M7E]=PXM%#( X+W7%2^O"B?:+10R)!"3H#63__XM5$(G'A=)TU8M5$(D"Z\Z0 +MC70F %6)Y8/L&(M%$(E$) B+10R)1"0$BT4(B00DZ!'___^%P'0$B>Q=P^CT +M#0 D)"0D%6)Y8/L&(M%#(E$) BX!0 (E$) 2+10B)!"3H3PH (GL7<.- +M="8 C;PG %6)Y8/L",<$) "+10B)1"0$Z+?___^)[%W#D)"058GE +M@^P8B5WXB77\Z%=A__^+,(G#QP0D) .CO#0 BU4(B<&%TG1&BP*) 8M" +M!(E!!(M""(E!"(M"#(E!#(M"$(E!$(M"%(E!%(M"&(E!&(M"'(E!'(M"((E! +M((G(B3.+7?B+=?R)[%W#C70F +K J04(Z[.)]HV\)P !5B>6+10B%P'0& +M78L PXGVN,"I!0CK\XGVC;PG %6)Y8M5"(72= >+10R) EW#NL"I!0CK +M\I"-M"8 58GE@^P(#[9-#(D<)(M%"(ET) 2+71"(RL#J!0^VTL'B H7 +MC70"!'0FBQ:#X1^#XP&)T-/X@^ !,Q=PXUT)@"- +MLL2I!0CKTI"-M"8 5;@% B>6#[!B)7?R+70B)1"0(QP0D (E< +M) 3HPU___SG8= >+7?R)[%W#@WT,!G7SN(*+!0CK[(UV %6)Y5=64X/L?,=% +MU #'1= QT7, ,=%R #H?F+__T@/E,"#?1@&#[; B47$ +M=S2+11C_)(77UW#L&[KOK!VZZ*P9NN>L'+KLH-]& %TLH-]& (/A0K_ +M__^+70PY7=1S"HMU"(M%U,8$,"?_1=2+50PY5=1S"HM-"(M=U,8$"US_1=2+ +M=0PY==1S"HM%"(M5U,8$ B?_1=3IQO[__X-]& $/A%K___^#?1@##X6R_O__ +MBU78@\(".U44#X.C_O__BW40BT78@'PP 3\/A9+^__\/MDPP @^^P8/H(8/X +M'0^'?O[___\DA?2-!0B)5=B+50PY5=2(3:AS"HM-"(M=U,8$"S__1=2+=0PY +M==1S"HM%"(M5U,8$ ES_1=2+30PY3=0/@VW___^+70B+==3&!!X_Z5[___^+ +M7<2%VP^$]P ,=%I $ #HK6#__P^V5:B+ ^W%%"!X@! ")5:"+1"+3:2+112)7"0,BW40 +MBUW8 B40D"(U%W(ET) 2)!"3HK%S__X7 B<=T,X/X_W1I@_C^=#B+ +M1=R)!"3H45[__X7 C4W@#Y3 7VDB0PD#[; 2"%%H.BW7O__A6#["B)??R+?1B)7?2)=?B%_W1"Z*U9__^+,(G# +MB7PD%(L'B40D$(M%%(E$) R+11")1"0(BT4,B40D!(M%"(D$).AW^?__B3.+ +M7?2+=?B+??R)[%W#O\"I!0CKMY!5B>575E.#[!SH6EG__XM]"(L A?^)1? / +MB# ! "+10@Y!32C!0AW8HG#0XTTW0 ")\,'H SG##X43 0 BPU HP4( +M@?DXHP4(#X34 B70D!#'VB0PDZ- % "C0*,%"(L5-*,%"(ET) 2-#-") +MV(D,)"G0P> #B40D".@C7/__B1TTHP4(H4"C!0B+50B+/-"+=- $BT44BU40 +MB7PD!(E$)!"+10R)5"0,B30DB40D".CE_O__.<=W4XUX 8L=0*,%"#' @?[ +MJ 4(BU4(#Y3 B3S3B7PD!$@A\(D$).A(!0 B<:+512+10B)=,,$BT40B50D +M$(M5#(E$) R)?"0$B50D"(DT).B._O__Z%%8__^+5?")$(/$'(GP6UY?7D$____Z)58___H +M2 0 )"-M"8 5;C J04(B>6#[!B)1"0,N/____^)1"0(BT4,B40D!(M% +M"(D$).AV_O__B>Q=PXGV58GE@^P(QP0D (M%"(E$) 3HM____XGL7<.- +M=@!5N0@ ")Y?R#[#B+50R)??R-?Q=P@0 B?:-O"< 58GE@^Q(B5W\BT4,C5W(B1PDB40D +M!.AU____@^P$N/____^)1"0(BT40B5PD#(E$) 2+10B)!"3HH_W__XM=_(GL +M7<.-M@ "-OP !5B>6#[$B)7?R+10R-7Q=PXVV +M (V_ %6)Y8/L2 ^^50R)7?RAP*D%"(U=R(D<)(E%R*'$J04(B50D!(E% +MS*'(J04(B470H"AW*D% +M"(E%Y*'@J04(B47HN $ ")1"0(Z&/U__^)7"0,N/____^)1"0(BT4(QP0D +M (E$) 3H@_S__XM=_(GL7<.-M@ "-OP !5N#H ")Y8/L"(E$ +M) 2+10B)!"3H1O___XGL7<.0D%6)Y5=64X/L#(MU$(M]#(7V>#J-M@ "- +MO"< B70D"(M%"(E\) 2)!"3HQ53__X7 B<-Y"NAZ5?__@S@$=-V#Q R) +MV%M>7UW#O@#@_W_KS)"0D)"0D)"0D)"0D%6)Y5=64X/L'(M%$(M="(MU#(UX +M%<9 %0"-M"8 B1PD,564X/L((M5#(M="(M-$(72BT44BW48#X34 B40D$+A8CP4(B4PD +M#(E4) B)1"0$B1PDZ"Q4___'!"0 N 4 ")1"0(N&2/!0B)1"0$Z"Y4 +M__^)1"0$B70D"(D<).C^4___BT,4.T,86#[!BAY*D%"(7 =5''!"0 N7N/ +M!0BX!0 (E,) 2)1"0(Z#M3__^)1"0,NDZ'!0@QP(E$) 2A,*,%"(E4) B) +M!"3H6U?__\<$) $ #H/U;__XVT)@ #_T.NKC;8 C;\ 58GE +M@^P(BT4(B00DZ)=3__^%P'0$B>Q=P^AR____B?95B>6#[ B+50B+10R)%"2) +M1"0$Z,!4__^%P'0$B>Q=P^A+____C70F (V\)P !5B>6#[ B+10R)1"0$ +MBT4(B00DZ-!5__^%P'0$B>Q=P^@;____D)"0D)"0D)"0D)!5B>575E.#[ R[ +M@ (MU".L-D)"0D)"0D)"0D)"0D(D<).A8____B40D!(G'B5PD"(DT).@^ +M4?__ALEQ@0X (GX@\0,6UY?7B1_O__D%6)Y8/L&(E=_(M="(D<).@4 +M4___0(D$).CC_O__B00DB5PD!.@_5O__BUW\B>Q=PU6)Y8/L$(EU^(M-#(MU +M"(E=](E]_(L^B?L/K]F)V)GW^;D! .<>)1?!T$XM=](G(BW7XBWW\B>Q= +MPXUT)@")'C')Z^>-=@"-O"< 58GE5U93@^P,BUT0BWT(BW4,2X/[_W0C +MD(VT)@ ")="0$B3PDZ(3___^%P+H! =0A+@_O_=>4QTH/$#(G06UY? +M7<.)]HV\)P !5B>575E.#[!R+=1"#_B0/AR<" "+?0R%_P^$% ( .AC +M4?__QP B<,QP(E$) R+10B)="0(B7PD!(D$).A24O__B47LBP.%P ^% +MUP$ (L?.UT(#X22 0 BUT8A=L/A+< "+'P^V X3 #X2J QT7H 0 +M ^^P+X ! B40D!(M%&(D$).CT3___A< /A (! "+11BY, (E,) 2) +M!"3HV$___X7 ="P/OD,!@_A$#X3] @_A$#X\% 0 @_A"#X3K C;8 +M C;PG ^^ X/H0H/X-0^'KP /\DA=B/!0BZ ( (E4) 2-1>R) +M!"3H5O[__XVV (G"A=*X P '4/BT7H 0>+1>R+512) C' @\0<6UY? +M7<.X 0 (E$) 3KPK@& C;0F (V\)P ")1"0(B70D!(U%[(D$ +M).A-_O__ZZNX!0 .ODN@@ ")5"0(Z]VX!P .O2N , #KR[@! +MZ\2X @ .N]NP0 ")7"0(Z[:+1>R+512) K@" Z7K___^Y @ (E, +M) 3I0?___\=%Z ( "^Z , .D1____@_AI#X4(____@'L"0@^%_O[__\=% +MZ , #I\O[__XGVBW48A?9T*0^V X3 ="*+51@/OL")1"0$B10DZ'A.__^% +MP'0,QT7L 0 .D^_O__N $ #I /___[@# Z?;^__^-??#IY/W__\<$ +M)*"/!0BXQH\%"(E$) RX=@ (E$) BXSH\%"(E$) 3HU4[__Y"0D)"0D)"0 +MD)"0D)!5B>575E.#[!R+10R+30B9B40D"(M$) B)5"0,BQF+?"0,BW$$]^,/ +MK_N)1>B)T 'XBWPD"(M5Z ^O_HD4) 'XB47LBTWLB4PD!.A!#P B?$QV#'1 +M"<&Z 0 '40BU7LBTT(BT7HB5$$,=*) 8/$'(G06UY?7<.0C;0F %6) +MY5=64X/L#(M=$(M]"(MU#$N#^_]T(Y"-M"8 B70D!(D\).A4____A<"Z +M 0 '4(2X/[_W7E,=*#Q R)T%M>7UW#B?:-O"< 58GE5U93@^PLBW4( +M@WT0) ^'0 ( (M]#(7_#X0M @ Z!)2__^+"(GSC;8 #[83#[;"]D1! +M 2!T T/K\(#Z+;@! #X3_ Z/5-___' ")PS' B40D#(M%$(E\ +M) 2)-"2)1"0(Z-1.__^)1>"+ XE5Y(7 #X7 0 BQ\Y\P^$=0$ (M=&(7; +M#X2D BQ\/M@.$P ^$EP ,=%W $ /OL"^ 0 (E$) 2+11B)!"3H +MA$S__X7 #X3G BT48N3 ")3"0$B00DZ&A,__^%P'0?#[Y# 8/X1 ^$ +MX@ (/X1 ^/Z@ (/X0@^$T ^^ X/H0H/X-0^'H0 /\DA?20!0BZ +M ( (E4) 2-1>")!"3H _[__XG"A=*X P '45BT7< 0>+1>"+5>2+312) +M 3' B5$$@\0L6UY?7<.X 0 (E$) 3KPK@& B40D"(ET) 2-1>")!"3H +M./[__^NSN 4 #KY+H( B50D".O=N < #KTK@# Z\NX 0 .O$ +MN ( #KO;L$ B5PD".NVBT7@BTT4BU7DB0&X @ .N%N0( ")3"0$ +MZ4_____'1=P" ON@# #I'____X/X:0^%%O___X![ D(/A0S____'1=P# +M Z0#___^+=1B%]G0P#[8#A,!T*8M-& ^^P(E$) 2)#"3H)4O__X7 =!/' +M1> ! QT7D .E4_O__N $ #I"?___[@# Z?_^__^-?>SIR_W_ +M_\<$)*"/!0BXYI %"(E$) RX=@ (E$) BXSH\%"(E$) 3H>TO__Y"0D%6) +MY8/L&(E=](M%"(M=#(EU^(E]_(D$).C[2O__B<8QP(7V= S\N0L ")W_.E +MB=B+7?2+=?B+??R)[%W#B?95B>575E.#[!2+12"%P ^$/@$ (M0%(M8'(E5 +M\(M0"(E=[(M8!(L B57HB5WDB47@BT4(P?@"]D4( XVPVP$ '4&C;#: 0 +MBT7PP?@"C;C; 0 ]D7P W4&C;C: 0 NQ^%ZU&)\(GQ]^N)\,'X'RG^P?H# +M*<*-!)*-!( IP<'I'XGX* .-1P$YPW12A=MX +M0XG8*?C1^(T$.(E%M(MU#(M5$(M%M(D&B50D!(DT)/]5"(7 B<)T%_RY"P +M (U]N(G&\Z6+?;3KM9"-="8 BUVTZZN)^"G8T?B-!!CKNX72#X5W____A?\/ +MA&_____\BT4,N0L "+51").(U%N(M]$(G&\Z7I4O___XUV %6)Y5>_!@ +M %8Q]E.![-P "+10B)O7C___^+50B+30B+ (M2!(M)"(F%=/___XM%"(F5 +ML?*=J-%#KVP@.)E5S___]U-K@?A>M1]^J+ +MA5S____!^@7!^!\IPHT$DHT$@,'@ CF%7/___P^%>P0 (G0@^ #2 ^$;P0 +M (T4-@'RC00;C126 =B-!((!R(N5:/___XM-$ ^WA #@D04(BPF-1 +_B858 +M____BX5T____B8U4____B850____BY50____P>@?2"'0@_@[B85T____?@N^ +M.P (FU=/___XNU5/___XN%7/____?>P?@"C9C; 0 ]H5<____ W4&C9C: +M 0 P;UH____"HG8NA^%ZU'WZHG9B=C!^!_1_\'Z RG"C022C02 *<&)R,'H +M'RG"*=.)T8N57/___\'Y HV,"R/^__^+G5C___^#ZD:-!-*-!,*-!("+E6S_ +M__\!V '(C01 C03"BXUP____B<+!X@0IPHT4D8G0BXUH____P> $*="+E73_ +M__^-!(*+E6S___\I\(F%?/___XU$.=W!^@X!T(N5)=82-=@"-O"< BTT,C56$C46XB50D!(D,)(E$) CHM_S__XE$)!B+ +ME7#___^-182)1"04BXUL____BX5T____B50D#(N57/___XE$)!"+A5C___^) +M3"0(B10DB40D!.@6^___BUV$B<$YPXF=3/___P^$,P$ #G[=#__C7C___]T +M'XE-A#' B?>#?=@ B[5,____#Y7 B85@____Z6C___^^_____X'$W (GP +M6UY?7<.-M@ Y\W2]BU78A=)X)(N%9/___X7 #XBZ B[UD____A?\/ +ME<"%T@^5PC'0J %TDHN%5/___XNU?/___XM5$ '8*?") HM%N#F%4/___W1H +MBXUT____,=*%R74%@_@\=&R+C5#___^+A73___^-/!DIPCG?C30Z#YS P>D? +M#[;8,Z2'___^Z 0 .N-,<"% +MT@^5P#F%8/___P^/V_[__^E$____D(UT)@"+1=@YA63___\/A##___^+E63_ +M__^%T@^((O___X7 #X@:____N' L"0"_D-/V_XF%2/___XF]1/___XNU1/__ +M_[______C00>,=(YV ^=PHE%@(GXP>@?.<)U-(N-2/___X/' H/_ 8TT3G[9 +M@:U$____<"P) ('!<"P) ('Y*/X%$(F-2/___WRPZ:_^__^+10R-38B-78") +M3"0(B5PD!(D$).B'^O__BY5D____.56H= 6+783KHXE<)!2+E73___^-18B) +M1"08BXUP____BX5L____B50D$(N56/___XE,) R+C5S___^)5"0$B40D"(D, +M).C6^/__B46$BTT,C56$B50D!(U%N(E$) B)#"3H&OK__XM=A.DA_O__]]KI +M^_S___?:Z=;\__^^ 0 .F'^___C78 C;PG %6XZ*D%"(GE@^P8B40D +M"+AP204(B40D!(M%"(D$).B=^O__B>Q=PY"0D)"0D)"0D%6)Y8/L&.BM +M@<%U4P VVT(V8%$[?__B77XB7W\W>'?X)YV&=W8W=@QP#'2BW7XBWW\B>Q= +MPXVT)@ #9??;9P3'V#[=%]MB)2.W__V8- QFB47TV6WTWWWHV6WVBT7H +MB<=05M\L)(/$"(7 >#W>ZMWIW^">=AK9X#'2V6WTWWWHV6WVBT7H*<89UXGP +MB?KKF=EM]-]]Z-EM]C'2BT7H <81U^OFC;8 V($0YO__Z[N+#"3#D)"0 +MD%6)Y5=6@^PHBWT,QT7H (MU"(M%$,=%[ "%_XM5%,=%] / +MB#\! "%T@^(* $ (EUX(72B?Z)1=2)5>1U>#GX=D2)^HM%X/=UU(G'C78 +MC;PG ,=%T ")?>B+5="+3?2+1>B)5>R%R8M5['0']]B#T@#WVH/$ +M*%Y?7<.0C70F (M]U(7_=0ZX 0 #'),=+W\8E%U(GP,=+W==2)1="+1>#W +M==2)Q^NNC;0F #E]Y'8+,?_KEXVT)@ /O47DB<>#]Q]U'3MUY'<. +MBU74,?\Y5> /@G+___^_ 0 .EH____BU7DN" ")^2GXB47@]< ]]_IKO[__Y"0D)"0D)"0D)"0D)!5B>57 +M5H/L,(M]#,=%X "+=0B+11#'1>0 A?^+513'1>P #XB_ 0 +MA=(/B*L! ")1B%_W0;QT7D (M%V(E%X(M%Z(M5X(M-Y(D0B4@$BT7LA7UW#C78 BT7,AB%]@^$8____XM%V(M5U(M-Z(E%X(M%X(E5Y(M5Y(D!B5$$Z43___^0C70F +M (M5W+@@ B?$I\(M]U(E%T-/BBT7,#[9-T-/HB?'39@]< ]]_I+O[__Y"0D)"0D)"0D)"0D)!5B>575H/L((M5%,=%\ +M "+=0B+?0S'1?0 BT40A=*)=>B)_HE%W(E5['5?.?AV*XGZBT7H]W7< +MB<>0QT78 (E]\(M5V(M%\(E5](M5](/$(%Y?7<.-=@"+1=R%P'4.N $ +M QR3'2]_&)1=R)\#'2]W7QV"S'_ +MZZ>-M"8 #[U%[(G'@_QW"HM5W#'_.57H BW4(BWT, +MQT7D (M%$(72B4WLB570 BT78B47@BT7LBU7@BTWDB1")2 2+1?"+5?2#Q#!>7UW# +MBW7,A?9U#;@! ,=+W=R)1>"+1>")5>2+5>2) 8E1!.E1____BU73IK/[__SM% +MV';"*WW,&U7/W__XV#>/W__RG"P?H".=9S'(G7C;0F (V\)P #_E+-X_?__1CG^ +M/W__XV3>/W__RG0P?@"A<"-53@^P$N[RD!0BAO*0%"(/X_W06C78 C;PG (/K!/_0BP.#^/]U +M]%A;7<-5B>53Z !;@7!E &QC ')C &5C &YO &9I &1I &QN '!I &)D &-D &UI &5X &1O !M; +M &T ,#$[,S0 ,#$[,S8 ,#$[,S4 ,#$[,S, ,#$[,S( )6(@)64@("59 "5B +M("5E("5(.B5- &5S8V%P90!D:7)E8W1O71EF4 =&EM92US='EL +M90!B;&]C:RUS:7IE &%U=&AO<@!H96QP '9EF]N=&%L &%C6QE "TM6QE "59+25M+25D +M( E62TE;2TE9" E2#HE33HE4RXE3B E>@ E62TE;2TE9" E2#HE30!I;G9A +M;&ED('1I;64@G! @.J 0(0*H$"$"J! @5J 0(0*H$"$"J! A J@0(0*H$"$"J +M! A J@0(0*H$"$"J! @4I@0('Z@$""FH! @XJ 0(0*H$"$>H! BGJ 0(NJ@$ +M".VH! A J@0(_*@$"".I! @JJ00(-*D$"$"I! A,J00(5JD$"&6I! AUJ00( +MA:D$")6I! B?J00(J:D$"#"J! A J@0(0*H$"$"J! A J@0(0*H$"$"J! A +MJ@0(3*H$"%BJ! B7J@0(&ZL$""NK! AVJP0(BJL$",^K! @4K 0(&ZP$""6L +M! AJK 0(KZP$")>Q! @2L@0(H+($",*R! @$LP0(,[($")"R! B0L@0(D+($ +M")"R! B0L@0(D+($")"R! B0L@0(D+($")"R! B0L@0(D+($")"R! B0L@0( +MD+($")"R! B0L@0(D+($")"R! B0L@0(D+($")"R! B0L@0(D+($")"R! B0 +ML@0(D+($")"R! B0L@0(D+($")"R! B0L@0(D+($")"R! B0L@0(D+($")"R +M! B0L@0(D+($")"R! B0L@0(D+($")"R! B0L@0(D+($")"R! B0L@0()+($ +M""2R! @DL@0()+($""2R! @DL@0()+($""2R! B0L@0(D+($")"R! B0L@0( +MD+($")"R! B0L@0(.K($")"R! B0L@0(D+($")"R! B0L@0(D+($")"R! B0 +ML@0(D+($")"R! B0L@0(D+($")"R! B0L@0(D+($")"R! B0L@0(D+($")"R +M! B0L@0(D+($")"R! B0L@0(D+($")6R! B0L@0(D+($")"R! B0L@0(D+($ +M")"R! A/L@0(D+($"%:R! A=L@0(D+($")"R! ADL@0(:[($")"R! B0L@0( +MD+($")"R! B0L@0(D+($")"R! ARL@0(D+($")"R! B0L@0(>;($")"R! B +ML@0(D+($"(>R! B0L@0(E;($"->R! C7L@0(U[($"->R! C7L@0(U[($"->R +M! C7L@0(U[($"->R! BWL@0(M[($"+>R! BWL@0(M[($"+>R! BWL@0(YK($ +M".:R! CFL@0(YK($".:R! CFL@0(M[($"+>R! BWL@0(M[($"+>R! BWL@0( +MM[($"+>R! BWL@0(M[($"+>R! BWL@0(M[($"+>R! BWL@0(M[($"+>R! BW +ML@0(M[($"+>R! BWL@0(M[($"+>R! BWL@0(M[($"+>R! CUL@0(];($"/6R +M! CUL@0(];($"/6R! AIS@0(=,\$")'/! C0S00(L,\$"-#/! AST 0(#= $ +M",G0! CR!S=')U8W0@;V)S=&%C:R!C;VYS=" J7U]O(#T@*"9D +M979?:6YO7V]B'1?9G)E92 M +M(%]?;RT^;V)J96-T7V)A2!@)7,@ +M+2UH96QP)R!F;W(@;6]R92!I;F9O2!B>2!D +M969A=6QT*2X*4V]R="!E;G1R:65S(&%L<&AA8F5T:6-A;&QY(&EF(&YO;F4@ +M;V8@+6-F='5355@@;F]R("TM2!A2 @(" @(" @(" @(&QI"P@8V]M +M;6%S("UM+"!H;W)I>F]N=&%L("UX+"!L;VYG("UL+ H@(" @(" @(" @(" @ +M(" @(" @(" @(" @(" @(" @2!O9B!GF4],4L* (" M;" @(" @(" @(" @(" @(" @(" @(" @ +M("!U7!E(" @(" @(" @(" @87!P96YD(&EN9&EC871O2!N86UE2!O=71S:61E('1H92!03U-)6"!L;V-A;&4*(" M=" @(" @(" @ +M(" @(" @(" @(" @(" @("!S;W)T(&)Y(&UO9&EF:6-A=&EO;B!T:6UE"B @ +M+50L("TM=&%B2P@86YD +M('-H;W" @(" @(" @(" @(" @(" @(" @(" @("!L +M:7-T(&5N=')I97,@8GD@;&EN97,@:6YS=&5A9"!O9B!B>2!C;VQU;6YS"B @ +M+5@@(" @(" @(" @(" @(" @(" @(" @(" @2!B>2!E;G1R>2!E>'1E;G-I;VX*(" M,2 @(" @(" @(" @(" @(" @(" @ +M(" @("!L:7-T(&]N92!F:6QE('!E2!T:&ES(&AE;' @86YD(&5X +M:70* @(" @(" M+79E&ET"@ "E-) +M6D4@;6%Y(&)E("AO2!D969A=6QT+"!C;VQO7!E2!I9B!S=&%N9&%R9"!O=71P=70@:7,@8V]N;F5C=&5D"G1O(&$@=&5R +M;6EN86P@*'1T>2DN"@!P0 E62TE;2TE9 E2#HE33HE4P#R^00( /D$" #Y! @ ^00( +M /D$" #Y! @ ^00( /D$" #Y! @ ^00( /D$" #Y! @ ^00( /D$" #Y! @ +M^00( /D$" #Y! @ ^00( /D$" #Y! @ ^00( /D$" #Y! @ ^00( /D$" #Y +M! @ ^00( /D$" #Y! @ ^00( /D$" #Y! @ ^00( /D$" #Y! @ ^00(5/@$ +M" #Y! @ ^00( /D$" #Y! @ ^00( /D$" #Y! @ ^00( /D$" #Y! @ ^00( +M /D$" #Y! @ ^00( /D$" #Y! @ ^00( /D$" #Y! @ ^00( /D$" #Y! @ +M^00( /D$" #Y! @ ^00( /D$"/WY! C]^00(E?L$"%;^! @ ^00(T?\$".$# +M!0CA_P0($ %" #Y! @ ^00( /D$"$ !0AP 4( /D$",@ !0@ ^00(_@ % +M" X!!0@W 04(1P$%".$#!0B0 04(\ $%" ("!0A @4( /D$" #Y! @ ^00( +M /D$" #Y! @ ^00(_?D$",,&!0CP 04(: ,%")(#!0@ ^00(X0,%",,&!0@ +M^00([08%"!8'!0A !P4(< <%"* '!0@ ^00(W@ %" #Y! @B^@0(, @%")P( +M!0@E"04( /D$"&<)!0CP 04(D0D%".<)!0@ " /\W,3#_T_;0_ +M ",@96YT-04( +MSSH%"'0U!0CP.@4(_#H%"/PZ!0@P-@4(9#@%"&0X!0AD. 4(9#@%"&0X!0AD +M. 4( #<%""(W!0@F-P4(9C<%"&HW!0AN-P4(6EN9R!C;VYD:71I;VYS+B @5&AE0"8 +8 U0#T +M !(!,0%/ 6X! @"\!&P,[*!$ 0 #TO___1!$ (3!__]D$0 +ME,/__X01 #$Q/__I!$ +M R*0%" @/____\! 0 $ +M " 4%H%" $ !36@4( ! (L% +M" $ BP4(!0 %5:!0@% 6UH%" ( !J6@4(!0 &%:!0@% +M9UH%" 4 !G6@4( 4 !M6@4(!0 &%:!0AS +M6@4(?5H%" $ #_____ 0 " 9!0@! 0 ! # J 4(.*,%"(". +M!0@4 %Z4@ !? @!&PP$!(@! < ' *BN__^# 0 $$. +M"(4"0@T%188$AP, !P \ &+#__P," 00X(A0)"#05%A@2' P +M' %P (LO__+0$ !!#@B% D(-!46&!(<# < ? !BS__^[ +M 0 $$."(4"0@T%2(8$AP, ! 0 $ ] 0 -< +M , L),$" T #0604(! $B!! @% X(H$" 8 !PA 0("@ +M &H$ + $ !4 P -"D!0@" P ( !0 1 +M%P /"0! @1 R) $"!( H $P @ #^__]O&) $"/___V\# +M \/__;TJ/! @ +M #_____ /____\ .2C!0@ +M -Z3! CNDP0(_I,$" Z4! @>E 0(+I0$"#Z4! A.E 0(7I0$"&Z4! A^E 0( +MCI0$")Z4! BNE 0(OI0$",Z4! C>E 0([I0$"/Z4! @.E00('I4$""Z5! @^ +ME00(3I4$"%Z5! ANE00(?I4$"(Z5! B>E00(KI4$"+Z5! C.E00(WI4$".Z5 +M! C^E00(#I8$"!Z6! @NE@0(/I8$"$Z6! A>E@0(;I8$"'Z6! B.E@0(GI8$ +M"*Z6! B^E@0(SI8$"-Z6! CNE@0(_I8$" Z7! @>EP0(+I<$"#Z7! A.EP0( +M7I<$"&Z7! A^EP0(CI<$")Z7! BNEP0(OI<$",Z7! C>EP0([I<$"/Z7! @. +MF 0('I@$""Z8! @^F 0(3I@$"%Z8! ANF 0(?I@$"(Z8! B>F 0(KI@$"+Z8 +M! C.F 0(WI@$".Z8! C^F 0(#ID$"!Z9! @NF00(/ID$"$Z9! ADH@4( +M +G-H6YS='( +F=N=2YV +M97)S:6]N "YG;G4N=F5R7H-"D%"0T1%1D=(24I+3$U.3U!1 +54E-455976%E:#0HP,3(S-#4V-S@Y +end diff --git a/tests/mailbox/main.cpp b/tests/mailbox/main.cpp new file mode 100644 index 00000000..9f1de6ac --- /dev/null +++ b/tests/mailbox/main.cpp @@ -0,0 +1,119 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include +#include +#include +#include +#include + +#include "../../src/vmime" +#include "../../examples/common.inc" + + + +std::ostream& operator<<(std::ostream& os, const vmime::text& txt) +{ + os << "["; + + for (int i = 0 ; i < txt.size() ; ++i) + { + const vmime::word& w = txt[i]; + + if (i != 0) + os << ","; + + os << "[" << w.charset().name() << "," << w.buffer() << "]"; + } + + os << "]"; + + return (os); +} + + +std::ostream& operator<<(std::ostream& os, const vmime::mailbox& mbox) +{ + std::cout << "MAILBOX[name=" << mbox.name() << ",email=" << mbox.email() << "]" << std::endl; + + return (os); +} + + +std::ostream& operator<<(std::ostream& os, const vmime::mailboxGroup& group) +{ + std::cout << "GROUP[name=" << group.name() << "]" << std::endl; + + for (int i = 0 ; i < group.size() ; ++i) + { + std::cout << "* " << group[i]; + } + + return (os); +} + + +int main(int argc, char* argv[]) +{ + // VMime initialization + vmime::platformDependant::setHandler(); + + + // Read data from standard input + std::ostringstream data; + std::istream* input = &std::cin; + std::ifstream file; + + if (argc >= 2) + { + file.open(argv[1], std::ios::in | std::ios::binary); + input = &file; + } + + while (!input->eof()) + { + char buffer[4096]; + input->read(buffer, sizeof(buffer)); + data.write(buffer, input->gcount()); + } + + // Parse address list and output results + vmime::addressList list; + list.parse(data.str()); + + for (int i = 0 ; i < list.size() ; ++i) + { + const vmime::address& addr = list[i]; + + if (addr.isGroup()) + { + const vmime::mailboxGroup& group = + dynamic_cast (addr); + + std::cout << group; + } + else + { + const vmime::mailbox& mbox = + dynamic_cast (addr); + + std::cout << mbox; + } + } +} diff --git a/tests/mailbox/run-test.sh b/tests/mailbox/run-test.sh new file mode 100755 index 00000000..7f5fbb2b --- /dev/null +++ b/tests/mailbox/run-test.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +TEST_DIR="./test-suites" +TEMP_DIR="/tmp" +PROGRAM="./main" + + +testFiles=`cd $TEST_DIR ; ls *.in` + +echo +echo Testing address parsing +echo ===================================================================== + +for testFile in $testFiles ; do + + testName=`echo $testFile | sed 's/\([^\.]*\)\.in/\1/'` + + printf %20s "$testName : " + + $PROGRAM < $TEST_DIR/$testFile > $TEMP_DIR/vmime_result + + diff="diff $TEMP_DIR/vmime_result $TEST_DIR/$testName.out" + res=`$diff` + + if [ "$res" = "" ] + then + echo "[OK]" + else + diffFile=$TEMP_DIR/vmime.mailbox.$testName.diff + echo "[NO: diff file is $diffFile]" + $diff > $diffFile + fi + +done + +echo + diff --git a/tests/mailbox/test-suites/test1.in b/tests/mailbox/test-suites/test1.in new file mode 100644 index 00000000..961feed7 --- /dev/null +++ b/tests/mailbox/test-suites/test1.in @@ -0,0 +1 @@ +My (this is a comment)name diff --git a/tests/mailbox/test-suites/test1.out b/tests/mailbox/test-suites/test1.out new file mode 100644 index 00000000..8f969f49 --- /dev/null +++ b/tests/mailbox/test-suites/test1.out @@ -0,0 +1 @@ +MAILBOX[name=[[us-ascii,My name]],email=me@somewhere.com] diff --git a/tests/mailbox/test-suites/test2.in b/tests/mailbox/test-suites/test2.in new file mode 100644 index 00000000..4b2c6284 --- /dev/null +++ b/tests/mailbox/test-suites/test2.in @@ -0,0 +1 @@ +mailbox1 ,;,,, ,, ,,;group1:mailbox1@group1, mailbox2@group2,,"mailbox #3" ;, ,,,,,,,,=?iso-8859-1?q?mailbox_number_3?= , =?abc?Q?mailbox?= =?def?Q?_number_4?= diff --git a/tests/mailbox/test-suites/test2.out b/tests/mailbox/test-suites/test2.out new file mode 100644 index 00000000..527b4baf --- /dev/null +++ b/tests/mailbox/test-suites/test2.out @@ -0,0 +1,8 @@ +MAILBOX[name=[[us-ascii,mailbox1]],email=mailbox@one] +GROUP[name=[[us-ascii,group1]]] +* MAILBOX[name=[],email=mailbox1@group1] +* MAILBOX[name=[],email=mailbox2@group2] +* MAILBOX[name=[[us-ascii,mailbox #3]],email=mailbox3@group2] +MAILBOX[name=[],email=mailbox@two] +MAILBOX[name=[[iso-8859-1,mailbox number 3]],email=mailbox@three] +MAILBOX[name=[[abc,mailbox],[def, number 4]],email=mailbox@four] diff --git a/tests/run-tests.sh b/tests/run-tests.sh new file mode 100755 index 00000000..9a9193d7 --- /dev/null +++ b/tests/run-tests.sh @@ -0,0 +1,5 @@ +#!/bin/sh +cd encoding ; ./run-test.sh ; cd .. +cd charset ; ./run-test.sh ; cd .. +cd mailbox ; ./run-test.sh ; cd .. +