--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <title>AsyncSocket Documentation</title>
+ <style type="text/css">
+ body {
+ line-height: 120%
+ }
+ h1 {
+ text-align: center;
+ font-family: sans-serif;
+ font-weight: bold;
+ font-size: 2em;
+ margin-bottom: 1.25em;
+ }
+ h2 {
+ font-family: sans-serif;
+ font-weight: bold;
+ font-style: normal;
+ font-size: 1.5em;
+ margin: 1.75em 0px 1em 0px;
+ }
+ h3 {
+ font-family: sans-serif;
+ font-weight: bold;
+ font-style: italic;
+ margin: 1.75em 0px 0.75em 0px;
+ }
+ p {
+ margin: 0px 0px 0.5em 0px;
+ }
+ ul {
+ margin: 0px 0px 0.5em 0px;
+ }
+ table {
+ border-collapse: collapse;
+ }
+ th {
+ font-family: sans-serif;
+ font-style: italic;
+ font-weight: normal;
+ vertical-align: top;
+ text-align: left;
+ border: 0px;
+ width: 7em;
+ }
+ td {
+ vertical-align: top;
+ border: 0px;
+ }
+ td.Argument {
+ width: 5em;
+ }
+ .Sample_Code {
+ white-space: pre;
+ background: #cccccc;
+ font-family: monospace;
+ margin: 0.75em 0px 0.75em 2em;
+ }
+ .Method_Heading {
+ font-family: sans-serif;
+ background: #cccccc;
+ margin: 1.5em 0px 0.75em 0px;
+ }
+ .Function {
+ white-space: nowrap;
+ }
+ .Class {
+ }
+ .Code {
+ font-family: monospace;
+ }
+ .Constant {
+ font-family: monospace;
+ }
+ .Filename {
+ font-style: italic;
+ }
+ .Vocabulary_Word {
+ font-style: italic;
+ }
+ .Argument {
+ font-style: italic;
+ }
+ </style>
+</head>
+<body style="margin: 1em 1em 1em 1em;">
+ <h1 style="text-align: center;">AsyncSocket Documentation</h1>
+ <a name="intro"></a>
+ <p>AsyncSocket is a TCP/IP socket networking library, designed to efficiently handle packet data. The library is in two files and one public Cocoa class.</p>
+ <p>The library is public domain, originally written by Dustin Voss, and now maintained by Deusty and the Cocoa community.</p>
+ <p>For support, visit the CocoaAsyncSocket Google code page at <a href="http://code.google.com/p/cocoaasyncsocket/">http://code.google.com/p/cocoaasyncsocket/</a></p>
+ <a name="cont"></a>
+ <h3>Contents</h3>
+ <ol>
+ <li><a href="#intro">Introduction</a>
+ <ul>
+ <li><a href="#cont">Contents</a></li>
+ <li><a href="#about">About AsyncSocket</a></li>
+ <li><a href="#doc">About This Document</a></li>
+ </ul>
+ </li>
+ <li><a href="#basics">Socket Basics</a>
+ <ul>
+ <li><a href="#limit">Socket Limitations</a></li>
+ <li><a href="#struct">Packet Structure</a></li>
+ </ul>
+ </li>
+ <li><a href="#using">Using AsyncSocket</a>
+ <ul>
+ <li><a href="#owner">Socket Ownership</a></li>
+ <li><a href="#deleg">Delegate Methods</a></li>
+ <li><a href="#conn">Accepting, Connecting, and Disconnecting</a></li>
+ <li><a href="#rw">Reading and Writing</a></li>
+ <li><a href="#parse">Parsing Packets</a></li>
+ <li><a href="#err">Error Handling</a></li>
+ <li><a href="#thread">Threading and Run-Loops</a></li>
+ <li><a href="#sub">Customizing AsyncSocket</a></li>
+ </ul>
+ </li>
+ <li><a href="#ref">AsyncSocket Reference</a>
+ <ul>
+ <li><a href="#api_alloc">Initialization and Deallocation Messages</a></li>
+ <li><a href="#api_ud">User Data Messages</a></li>
+ <li><a href="#api_deleg">Delegation Messages</a></li>
+ <li><a href="#api_conn">Connection Messages</a></li>
+ <li><a href="#api_rw">Read and Write Messages</a></li>
+ <li><a href="#api_delim">Delimiter Messages</a></li>
+ <li><a href="#api_debug">Debugging and Customization Messages</a></li>
+ <li><a href="#api_delegmeth">AsyncSocketDelegate Methods</a></li>
+ <li><a href="#api_err">Errors</a></li>
+ </ul>
+ </li>
+ <li><a href="#new">API Changes</a></li>
+ <li><a href="#idx">API Index</li>
+ </ol>
+
+ <a name="about"></a>
+ <h3>About AsyncSocket</h3>
+ <p>
+ The Cocoa API provides a handful of networking classes, suitable for downloading and uploading images and files.
+ These classes support HTML, FTP, and other protocols, but cannot be used with application-specific protocols.
+ Without low-level socket classes supporting application-specific protocols,
+ developers must custom-code networking solutions using BSD or Carbon functions in conjunction with
+ <span class="Class">NSFileHandle</span>,
+ <span class="Class">NSInputStream</span>, and
+ <span class="Class">NSOutputStream</span>.
+ These functions and classes are not optimized for TCP/IP networking in a real Cocoa application, and can be difficult to integrate.
+ </p>
+ <p>
+ AsyncSocket provides easy-to-integrate “fire and forget” networking that makes it easy for your application to support networking.
+ Features include:</p>
+ <ul>
+ <li>
+ Queued non-blocking reads and writes, with optional timeouts.
+ You tell AsyncSocket what to read or write and get out of the way.
+ It will call you when it's done.
+ </li>
+ <li>
+ Automatic socket acceptance. If you tell AsyncSocket to accept connections, it will call you with new instances of itself for each connection.
+ You can, of course, disconnect them immediately.
+ </li>
+ <li>
+ Delegate support. Errors, connections, accepts, read completions, write completions, and disconnections all result in a message to your delegate.
+ </li>
+ <li>
+ Run-loop based, not thread based. Although you can use AsyncSocket on main or worker threads, you are not forced to do so.
+ <span class="Class">AsyncSocket</span> will sent messages to the delegate asynchronously via the run-loop.
+ The messages include a <span class="Argument">socket</span> argument, allowing you to distinguish between many instances of <span class="Class">AsyncSocket</span>.
+ </li>
+ <li>
+ Self-contained in one class. You do not need to muck around with a collection of stream or socket instances. The class handles all of that.
+ </li>
+ <li>
+ Support for TCP streams. <span class="Class">AsyncSocket</span> does not support UDP or multicast sockets.
+ </li>
+ <li>
+ Based on Apple’s own <span class="Class">CFSocket</span> and <span class="Class">CFStream</span> Carbon APIs.
+ </li>
+ </ul>
+
+ <a name="doc"></a>
+ <h3>About This Document</h3>
+ <p>This document assumes the reader already has the general public’s understanding of networking, and a developer’s understanding of Cocoa and Objective-C programming.</p>
+
+ <a name="basics"></a>
+ <h2>Socket Basics</h2>
+ <p>In networking parlance, a computer is a <span class="Vocabulary_Word ">host</span> for a number of sockets. A <span class="Vocabulary_Word ">socket</span> is one end of a communication channel called a <span class="Vocabulary_Word ">network connection</span>; the other end is another socket. From its own point of view, any socket is the <span class="Vocabulary_Word ">local socket</span>, and the socket at the other end of the connection is the <span class="Vocabulary_Word ">remote socket.</p>
+ <p>To establish the connection, one of the two sockets, the <span class="Vocabulary_Word ">connect socket</span>, must contact the other socket, the <span class="Vocabulary_Word ">listen socket</span>, and the listen socket must <span class="Vocabulary_Word ">accept</span> the connection. To contact the listen socket, the connect socket must know its <span class="Vocabulary_Word ">socket address</span>. Every socket has a socket address. The address consists of two parts: the <span class="Vocabulary_Word ">host address</span> and the <span class="Vocabulary_Word ">port number</span>. The host address is the IP address of the computer, and the port number uniquely identifies each socket hosted on the computer.</p>
+ <p>A computer can have multiple host addresses. It will have a pair of addresses for each possible connection method (<i>e.g.</i>, an Ethernet card, a modem, an AirPort card, a VPN connection) and a pair for connecting to itself (called “localhost”). One address of each pair is an IPv4 address such as “192.168.3.1,” and the other is an IPv6 address such as “fe80::230:65ff:fe29:aa9d.”</p>
+ <p>An address such as “www.google.com” corresponds to a host address, but it is not a host address itself. It is a <span class="Vocabulary_Word ">DNS address</span> or <span class="Vocabulary_Word ">DNS name</span>, which is converted to a host address by a <span class="Vocabulary_Word ">DNS look-up</span> operation. A <span class="Vocabulary_Word ">URL</span> such as “http://www.google.com:80” is likewise not a host address. URLs can include a DNS name, host address, port number, and other information.</p>
+ <p>Applications running on different hosts, or even on the same host, can use sockets to communicate with each other. Looking at it another way, each socket provides a communication service to its <span class="Vocabulary_Word ">client application</span>. The applications send and receive data to and from each other, which they can interpret and act upon. The data is composed of bytes, arranged into groups called <span class="Vocabulary_Word ">packets</span> and sent and received in accordance with a <span class="Vocabulary_Word ">protocol</span> followed by both applications.</p>
+ <p>A protocol establishes the structure of each packet, the circumstances under which any particular packet should be sent, and rules to handle exceptional circumstances. It also establishes roles for each client application. In a <span class="Vocabulary_Word ">client-server architecture</span>, some applications (the <span class="Vocabulary_Word ">servers</span>) provide a service used by other applications (the <span class="Vocabulary_Word ">clients</span>). In a <span class="Vocabulary_Word ">peer-to-peer architecture</span>, some applications (the <span class="Vocabulary_Word ">peers</span>) act as clients and servers at the same time.</p>
+
+ <a name="limit"></a>
+ <h3>Socket Limitations</h3>
+ <p>In some ways, a socket is like a file. Both contain data with a beginning and an end. Both can be written to or read from. But in other ways they differ, and these differences drive the design of a protocol.</p>
+ <p>First, while a file is typically for either reading or writing, a socket is interactive. Applications must be able to interrupt and alert each other, changing each others’ behavior. For this reason, data is divided up into packets, and this division leaves an opening for interruptions.</p>
+ <p>Second, while a file has a known size, a socket does not have a size. An application cannot know how much data is left in the current packet, unless the protocol itself specifies this. Thus, all packets include implicit or explicit size information or markers to indicate when the packet is finished.</p>
+ <p>Third, while a file is reliable, a socket is not reliable. When you read from a socket, the data arrives in chunks, with possibly large delays between each chunk, and there is no way to know whether a particular delay is because of high traffic or because of an unexpected disconnection. So applications are forced to treat a long delay as if it was a disconnection, and protocols define time-outs and retry messages to regulate this.</p>
+ <p>The AsyncSocket library was designed to make these protocol considerations easy to deal with.</p>
+
+ <a name="struct"></a>
+ <h3>Packet Structure</h3>
+ <p>Network communication protocols employ certain basic elements common to all protocols. These are:</p>
+ <ul>
+ <li>
+ <p>A <span class="Vocabulary_Word ">field</span>. This is the fundamental component of a packet. A field is a sequence of bytes, usually short, that are interpreted as a unit. A field may be represent a number, a character or sequence of characters, binary data, an enumeration, or a series of bit flags.</p>
+ <p>If field is numeric and more than one byte long, the protocol must specify whether the bytes of the number should be arranged in “little-endian,” “big-endian,” or “network” byte order. Carbon and Cocoa both provide byte-order functions that can handle different byte orders.</p>
+ <p>A field may be fixed-length or variable-length. The length of a <span class="Vocabulary_Word ">fixed-length field</span> is specified by the protocol. The length of a <span class="Vocabulary_Word ">variable-length field</span> is specified either explicitly or implicitly. In the former case, the variable-length field (the <span class="Vocabulary_Word ">data field</span>) will be preceded by a fixed-length numeric field (the <span class="Vocabulary_Word ">length field</span>) that specifies the length of the data field. In the latter case, a byte sequence (the <span class="Vocabulary_Word ">delimiter</span>) will mark the end of the field.</p>
+ </li>
+ <li>
+ <p>A <span class="Vocabulary_Word ">line of text</span>. This is character data, typically encoded as ASCII. A <span class="Vocabulary_Word ">line-ending</span> marks the end of the line. There are three commonly-used line endings: CR, LF, and CRLF. The protocol must specify which is to be used. Many text-based protocols sub-divide a line of text into variable-length fields delimited by spaces and other characters.</p>
+ </li>
+ <li>
+ <p>A <span class="Vocabulary_Word ">packet</span>, consisting of a <span class="Vocabulary_Word ">packet header</span> and a <span class="Vocabulary_Word ">payload</span>. The packet header contains fixed-length fields describing the type (and possibly length) of the payload. The payload contains a series of variable-length and fixed-length fields, which vary according to the payload type.</p>
+ </li>
+ <li>
+ <p>A <span class="Vocabulary_Word ">data stream</span>, a continuous sequence of bytes. The end of the data stream is marked by the closing of the connection. A data stream can be considered one enormous unstructured packet. When an application is transmitting a data stream, protocols typically do not allow the receiving application to interrupt the transmitting application.</p>
+ <p>A data stream may be preceded by a <span class="Vocabulary_Word ">data stream header</span>, a series of variable-length and fixed-length fields that describe the data stream.</p>
+ </li>
+ </ul>
+ <p>“<a href="#parse">Parsing Packets</a>” describes how to use <span class="Class">AsyncSocket</span> methods to read these different elements.</p>
+
+ <a name="using"></a>
+ <h2>Using AsyncSocket</h2>
+ <p>The AsyncSocket library is composed of one class, also called <span class="Class">AsyncSocket</span>. An instance of <span class="Class">AsyncSocket</span> represents one socket, which may be a listen socket or a connect socket. An instance may also be disconnected, in which case it does not represent a functioning socket. Throughout this document, the terms “socket” and “instance of <span class="Class">AsyncSocket</span>” are used almost interchangeably.</p>
+ <p>To use the <span class="Class">AsyncSocket</span> class in your project, add <span class="Filename ">AsyncSocket.m</span> and <span class="Filename ">/System/Library/Frameworks/CoreServices.framework</span> to the project, and import <span class="Filename ">AsyncSocket.h</span> into each file that needs it.</p>
+ <p>This version of AsyncSocket requires Mac OS X 10.4 or later. If you must support Mac OS X 10.3, you may use AsyncSocket version 3.13.</p>
+
+ <a name="owner"></a>
+ <h3>Socket Ownership</h3>
+ <p>In a client-server architecture, an application acting as a client usually employs one connect socket to communicate with a server, and an application acting as a server usually employs one listen socket to accept connections from clients and several connect sockets to communicate with the connected clients. In a peer-to-peer architecture, an application usually employs one listen socket and several connect sockets as if it were a server.</p>
+ <p>Each socket should be managed by a <span class="Vocabulary_Word ">connection controller</span> class. This class should be responsible for:</p>
+ <ul>
+ <li>Owning the local socket of the network connection.</li>
+ <li>Constructing and writing outgoing packets.</li>
+ <li>Reading and parsing incoming packets.</li>
+ <li>Detecting and handling error conditions.</li>
+ </ul>
+ <p>A collection of connected sockets should be managed by a <span class="Vocabulary_Word ">connection array controller</span>. This class should be responsible for creating and destroying individual connection controllers as needed. Each managed connection controller should keep the connection array controller apprised of its status.</p>
+ <p>If an application has a listen socket, it should be owned and managed by the connection array controller. When the listen socket accepts a connection, the connection array controller should construct a new connection controller responsible for managing the new connection.</p>
+
+ <a name="deleg"></a>
+ <h3>Delegate Methods</h3>
+ <p>An instance of <span class="Class">AsyncSocket</span> sends messages to the its delegate object upon completing certain operations or encountering certain errors. All instances of <span class="Class">AsyncSocket</span> should have a delegate that responds to these messages appropriately. The delegate object should be the socket’s connection controller or connection array controller.</p>
+ <p>The delegate object should implement the following delegate methods according to the socket’s purpose (see “<a href="#ref">AsyncSocket Reference</a>” below for detailed descriptions):</p>
+ <ul>
+ <li>
+ <p>All delegates should implement <a class="Function" href="#api_sockdiddisconnect">-onSocketDidDisconnect:</a> for clean-up.</p>
+ </li>
+ <li>
+ <p>All delegates should implement <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> for error recovery and logging.</p>
+ </li>
+ <li>
+ <p>Delegates controlling listen sockets should implement <a class="Function" href="#api_sockdidacceptnewsock">-onSocket:didAcceptNewSocket:</a>. Delegates should also implement <a class="Function" href="#api_sockwantsrunloopnewsock">-onSocket:wantsRunLoopForNewSocket:</a> if connections should be directed to individual threads.</p>
+ </li>
+ <li>
+ <p>Delegates controlling connect sockets or accepted connections should implement <a class="Function " href="#api_sockdidconnecthostport">-onSocket:didConnectToHost:port:</a>, <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a>, and <a class="Function" href="#api_sockdidwritedatatag">-onSocket:didWriteDataWithTag:</a>. Delegates should also implement <a class="Function" href="#api_sockwillconnect">-onSocketWillConnect:</a> if the socket’s underlying Cocoa objects need to be customized.</p>
+ </li>
+ </ul>
+ <p>You will seldom need to change a socket’s delegate, but should the need arise, be careful. If a socket completes any read or write operations initiated under the old delegate, the new delegate will be notified, not the old delegate. You can check for pending read or write operations by sending <a class="Function" href="#api_cansafelysetdelegate">-canSafelySetDelegate:</a> to the socket. See “<a href="#rw">Reading and Writing</a>” below for a discussion of pending read or write operations.</p>
+ <p>Several instances of <span class="Class">AsyncSocket</span> can safely share one delegate object. Each instance passes itself as the first argument of any delegate message it sends. This allows the delegate object to distinguish between <span class="Class">AsyncSocket</span> instances.</p>
+
+ <a name="conn"></a>
+ <h3>Accepting, Connecting, and Disconnecting</h3>
+ <p>To initiate a connection to a remote socket at a given socket address, send <a class="Function" href="#api_connecthostporterror">-connectToHost:onPort:error:</a> to the socket, passing the host address and port as arguments. The host address can be an IP address or DNS name, including “localhost.” A DNS name does not include a port number.</p>
+ <p>To set up a listen socket for a given port, send <a class="Function" href="#api_acceptporterror">-acceptOnPort:error:</a> to the socket. The socket will listen on all available host addresses. To set up a socket that listens on only one address, send <a class="Function" href="#api_acceptaddressporterror">-acceptOnAddress:port:error:</a>. To optionally direct an incoming connection to a particular thread, implement <a class="Function" href="#api_sockwantsrunloopnewsock">-onSocket:wantsRunLoopForNewSocket:</a> in the socket’s delegate.</p>
+ <p>To alter the properties of the socket’s underlying <a class="Class">CFReadStream</a> or <a class="Class">CFWriteStream</a> objects (for example, to support SSL connections) implement <a class="Function" href="#api_sockwillconnect">-onSocketWillConnect:</a> in the socket’s delegate.</p>
+ <p>To disconnect the socket cleanly, send <a class="Function" href="#api_disconnectafterwriting">-disconnectAfterWriting</a> to the socket. This method will complete all write operations before disconnecting. To disconnect immediately, send <a class="Function" href="#api_disconnect">-disconnect</a>. In either case, the socket will send <a class="Function" href="#api_sockdiddisconnect">-onSocketDidDisconnect:</a> to the delegate after it finishes disconnecting.</p>
+ <p>If the remote socket cleanly disconnects from the local socket, the local socket will send <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> to the delegate. The message’s <span class="Argument">error</span> argument will be <span class="Constant">nil</span> (see “<a href="#err">Error Handling</a>” below). The local socket will then send <a class="Function" href="#api_sockdiddisconnect">-onSocketDidDisconnect:</a> to the delegate.</p>
+
+ <a name="rw"></a>
+ <h3>Reading and Writing</h3>
+ <p><span class="Class">AsyncSocket</span> handles reading and writing operations itself. You specify the operation you desire, and <span class="Class">AsyncSocket</span> carries it out as soon as possible and sends a message to the delegate when the operation is complete.</p>
+ <p><span class="Class">AsyncSocket</span> supports three reading operations (“read-to-length,” “read-to-data,” and “read-any”) and one write operation (“write-data”).</p>
+ <ul>
+ <li>
+ <p>The “read-to-length” operation reads a certain number of bytes from the remote socket. To perform this operation, send <a class="Function" href="#api_readdatalengthtimeouttag">-readDataToLength:withTimeout:tag:</a> to the socket.</p>
+ </li>
+ <li>
+ <p>The “read-to-data” operation reads all bytes up to (and including) a delimiter sequence. To perform this operation, send <a class="Function" href="#api_readdatadatatimeouttag">-readDataToData:withTimeout:tag:</a> to the socket.</p>
+ </li>
+ <li>
+ <p>The “read-any” operation reads the first available bytes. To perform this operation, send <a class="Function" href="#api_readdatatimeouttag">-readDataWithTimeout:tag:</a> to the socket.</p>
+ </li>
+ <li>
+ <p>The “write-data” operation writes a data object to the remote socket. To perform this operation, send <a class="Function" href="#api_writedatatimeouttag">-writeData:withTimeout:tag:</a> to the socket.</p>
+ </li>
+ </ul>
+ <p>Invoking a read or write method will perform the requested operation immediately, if possible. But if the operation cannot be completed immediately, it will be placed in a read or write queue. <span class="Class">AsyncSocket</span> will continue to execute the queued operations in sequential order in the background.</p>
+ <p>To check the progress of the currently-executing “read-to-length” or “write-data” operation, you may send <a class="Function" href="#api_progressreadtagbytesdonetotal">-progressOfReadReturningTag:bytesDone:total:</a> or <a class="Function" href="#api_progresswritetagbytesdonetotal">-progressOfWriteReturningTag:bytesDone:total:</a> to the socket. These methods return a decimal number between 0.0 and 1.0, and you may also get the number of bytes read or written of the total.
+ <p>The read and write operations support optional time-outs. The time-out value is a decimal number of seconds, for example, 1.5. The time-out value specifies how long the operation can take once begun. A queued operation does not start running out of time until it begins execution. <span class="Class">AsyncSocket</span> will use the system-defined time-out value if you send a negative time-out argument.</p>
+ <p>If an operation runs out of time before it is completed, the socket will disconnect. If your protocol supports a mechanism to recover from a long-delayed transmission, you can supply your own “soft” time-out by using <span class="Class">NSTimer</span> objects instead of or in addition to AsyncSocket’s own time-out system.</p>
+ <p>When an operation has completed within the allotted time, the socket will send a message to its delegate (either <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> or <a class="Function" href="#api_sockdidwritedatatag">-onSocket:didWriteDataWithTag:</a>). The delegate object should respond appropriately, sending another read or write message to the socket as necessary.</p>
+ <p>To help the delegate object respond appropriately, you can supply a tag value as an argument of a read or write message. A tag value can be an integer, pointer, or object id. Tag values are not transmitted to the remote socket, and the remote socket does not transmit tag values to the local socket. But the message sent by the local socket to its delegate upon completing the operation includes the tag value you initially supplied. The tag value can be used to “remember” the circumstances under which the operation was initiated. Tag values can mark a type of operation, or specify a step of a multi-operation process.</p>
+
+ <a name="parse"></a>
+ <h3>Parsing Packets</h3>
+ <p>To parse a packet, or to read a data stream, line of text, or data stream header, consider how to break it down into a series of simple read operations.</p>
+ <p>Here are some techniques you can use to read and parse the elements of a protocol:</p>
+ <ul>
+ <li>
+ <p>Any individual fixed-length field can be read by a “read-to-length” operation. But you can also read a series of fixed-length fields all at once. The fields will be collected in a single data object. You can easily recover individual fields by using a C struct, as in this sample code:</p>
+ <div class="Sample_Code ">
+UInt32 field1, field2;
+struct fields { UInt32 field1; UInt32 field2; };
+
+struct fields *fieldSeries = (struct fields *)[receivedData bytes];
+field1 = fieldSeries->field1;
+field2 = fieldSeries->field2;</div>
+ <p>If only fixed-length fields comprise a packet, you can use this technique to read an entire packet at once.</p>
+ </li>
+ <li>
+ <p>Any delimited variable-length field can be read in its entirety by a single “read-to-data” operation.</p>
+ </li>
+ <li>
+ <p>A variable-length data field preceded by a fixed-length length field can be read in two parts. Use one “read-to-length” operation to read the length field. This will tell you how long the data field is. Use another “read-to-length” operation to read the data field.</p>
+ </li>
+ <li>
+ <p>A packet can also be read in two (or more) parts. Use one “read-to-length” operation to read the entire fixed-length packet header. Extract the length of the payload from the header, and use the above techniques to read the fields of the payload.</p>
+ </li>
+ <li>
+ <p>A line of text can be treated as a variable-length field delimited by a line ending. <span class="Class">AsyncSocket</span> provides delimiter messages that return the CR, LF, and CRLF line endings. Your client application will receive the line of text as a data object. You can then convert the data object to a string object, and use the <span class="Class">NSString</span> and <span class="Class">NSScanner</span> classes to break the line into individual fields.</p>
+ </li>
+ <li>
+ <p>Data stream headers vary in format, but can be treated as fixed- or variable-length fields and parsed using the above techniques.</p>
+ </li>
+ <li>
+ <p>A data stream can be read with one of two techniques. The first technique is appropriate when the data in the stream must be processed as it arrives, or if you do not know the size of the data:</p>
+ <ol>
+ <li>
+ <p>Send <a class="Function" href="#api_readdatatimeouttag">-readDataWithTimeout:tag:</a> to the socket.</p>
+ </li>
+ <li>
+ <p>When data arrives, the socket will send send <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> to the delegate. Process or store the data.</p>
+ </li>
+ <li>
+ <p>Send <a class="Function" href="#api_readdatatimeouttag">-readDataWithTimeout:tag:</a> to the socket before returning from the delegate method.</p>
+ </li>
+ <li>
+ <p>Repeat until the connection closes. In the delegate object’s <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> method, send <a class="Function" href="#api_readdatatimeouttag">-readDataWithTimeout:tag:</a> to the socket one last time.</p>
+ </li>
+ </ol>
+ <p>The second technique can only be used when you know the size of the data stream and want the entire stream returned in a single data object:</p>
+ <ol>
+ <li><p>Send <a class="Function" href="#api_readdatalengthtimeouttag">-readDataToLength:withTimeout:tag:</a> to the socket.</p></li>
+ <li><p>Wait for the delegate object’s <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> method to be called.</p></li>
+ </ol>
+ </li>
+ </ul>
+
+ <a name="err"></a>
+ <h3>Error Handling</h3>
+ <p>
+ If a socket encounters an input/output error, or if a read or write operation times out, AsyncSocket assumes the connection has been broken and must be re-established.
+ The socket will proceed to disconnect itself. The remote socket will typically be disconnected by its own client application.
+ </p>
+ <p>
+ Before disconnecting the local socket, an <span class="Class">AsyncSocket</span> instance will
+ send <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> to its delegate object.
+ The second argument of this message is an <span class="Class">NSError</span> object.
+ This object may contain a POSIX, Mac OS, or <span class="Class">CFStream</span> error code,
+ as indicated by the object’s <span class="Function ">-domain</span> method.
+ It may also contain an <span class="Class">AsyncSocket</span>-specific error code.
+ See “<a href="#api_sockdomain">Errors</a>” below for details.
+ </p>
+ <p>
+ During the execution of its <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> method,
+ the delegate object may retrieve all unreceived data (including data from any partially-completed read operations) by
+ sending <a class="Function" href="#api_unreaddatatag">-unreadData</a> to the socket.
+ </p>
+ <p>
+ After the delegate object’s <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> method returns,
+ the socket will be disconnected.
+ It will then send <a class="Function" href="#api_sockdiddisconnect">-onSocketDidDisconnect:</a> to the delegate.
+ </p>
+
+ <a name="thread"></a>
+ <h3>Threading and Run-Loops</h3>
+ <p>An <span class="Class">AsyncSocket</span> instance must be used in conjunction with a run-loop. Its run-loop activity occurs in the <span class="Constant">NSDefaultRunLoopMode</span> mode.</p>
+ <p>An <span class="Class">AsyncSocket</span> instance may be used from a worker thread or the main thread. However, each instance should only be used from one thread, so that the instance does not invoke delegate methods from within the wrong thread context.</p>
+ <p>To create a connect or listen socket in a particular thread, simply create the socket in the context of that thread.</p>
+ <p>A listen socket creates a new socket when it accepts a connection. To ensure this new socket is created in a particular thread, return the thread’s run-loop from the listen socket’s <a class="Function" href="#api_sockwantsrunloopnewsock">-onSocket:wantsRunLoopForNewSocket:</a> delegate method.</p>
+
+ <a name="sub"></a>
+ <h3>Customizing AsyncSocket</h3>
+ <p><span class="Class">AsyncSocket</span> is not intended to be sub-classed. However, since <span class="Class">AsyncSocket</span> is in the public domain, you may alter it as much as you like for your own projects. The source code was written to be understandable.</p>
+ <p><span class="Class">AsyncSocket</span> uses <span class="Class">CFSocket</span>, <span class="Class">CFReadStream</span>, and <span class="Class">CFWriteStream</span> internally. You may access these directly and set whatever properties or behaviors you may need. The <a class="Function" href="#api_sockwillconnect">-onSocketWillConnect:</a> delegate method is designed to facilitate this.</p>
+
+ <a name="ref"></a>
+ <h2>AsyncSocket Reference</h2>
+ <p>See “<a href="#idx">API Index</a>” below for an alphabetical list of messages, methods, types, and constants.</p>
+
+ <a name="api_alloc"></a>
+ <h3>Initialization and Deallocation Messages</h3>
+ <p><span class="Class">AsyncSocket</span> does not provide an auto-released convenience initializer.</p>
+ <p><a class="Function" href="#api_init">-init</a> <a class="Function" href="#api_initdelegate">-initWithDelegate:</a> <a class="Function" href="#api_initdelegateuserdata">-initWithDelegate:userData:</a> <a class="Function" href="#api_dealloc">-dealloc</a></p>
+
+ <a name="api_init"></a>
+ <p class="Method_Heading">-init</p>
+ <p>This message initializes the receiver, without setting a delegate.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(id)init</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td><p>An instance of <span class="Class">AsyncSocket</span>.</p></td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_initdelegate"></a>
+ <p class="Method_Heading">-initWithDelegate:</p>
+ <p>This message initializes the receiver, setting the delegate at the same time.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(id)initWithDelegate:(id)delegate</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments<p></th>
+ <td class="Argument"><p>delegate</p></td>
+ <td>
+ <p>An object that will act as the delegate for the receiver. The delegate should implement the necessary <span class="Class">AsyncSocketDelegate</span> methods.</p>
+ <p>May be <span class="Constant">nil</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p>
+ <td colspan="2">
+ <p>An instance of <span class="Class">AsyncSocket</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td colspan="2"><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_initdelegateuserdata"></a>
+ <p class="Method_Heading">-initWithDelegate:userData:</p>
+ <p>This message initializes the receiver, setting the delegate and user data at the same time. This method is the designated initializer of an AsyncSocket instance.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(id)initWithDelegate:(id)delegate userData:(long)userData</p></td>
+ </tr>
+ <tr>
+ <th rowspan="2"><p>Arguments<p></th>
+ <td class="Argument"><p>delegate</p></td>
+ <td>
+ <p>An object that will act as the delegate for the receiver. The delegate should implement the necessary <span class="Class">AsyncSocketDelegate</span> methods.</p>
+ <p>May be <span class="Constant">nil</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>userData</p></td>
+ <td>
+ <p>A value that will be associated with the receiver. It may be retrieved later through a <a class="Function" href="#api_userdata">-userData:</a> message.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p>
+ <td colspan="2">
+ <p>An instance of <span class="Class">AsyncSocket</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td colspan="2"><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_dealloc"></a>
+ <p class="Method_Heading">-dealloc</p>
+ <p>This message will deallocate the receiver, disconnecting if necessary.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td><span class="Body Code ">-(void)dealloc</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_ud"></a>
+ <h3>User Data Messages</h3>
+ <p><a class="Function" href="#api_userdata">-userData</a> <a class="Function" href="#api_setuserdata">-setUserData:</a></p>
+
+ <a name="api_userdata"></a>
+ <p class="Method_Heading">-userData</p>
+ <p>This message returns the receiver’s current user data, an arbitary value associated with the receiver.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(long)userData</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td><p>The current user data.</p></td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_setuserdata"></a>
+ <p class="Method_Heading">-setUserData:</p>
+ <p>This message sets the receiver’s user data, an arbitary value associated with the receiver.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(void)setUserData:(long)userData</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td class="Argument"><p>userData</p></td>
+ <td>
+ <p>A value that will be associated with the receiver.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td colspan="2"><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td colspan="2"><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_deleg"></a>
+ <h3>Delegation Messages</h3>
+ <p><a class="Function" href="#api_delegate">-delegate</a> <a class="Function" href="#api_setdelegate">-setDelegate:</a> <a class="Function" href="#api_cansafelysetdelegate">-canSafelySetDelegate</a></p>
+
+ <a name="api_delegate"></a>
+ <p class="Method_Heading">-delegate</p>
+ <p>This message returns the receiver’s current delegate object.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(id)delegate</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td><p>The current delegate object, or <span class="Constant">nil</span>.</p></td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_setdelegate"></a>
+ <p class="Method_Heading">-setDelegate:</p>
+ <p>This message sets the receiver’s delegate object. The delegate object is not retained.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(void)setDelegate:(id)delegate</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td class="Argument"><p>delegate</p></td>
+ <td>
+ <p>An instance of a class that will act as the delegate for the receiver. Should implement the necessary <span class="Class">AsyncSocketDelegate</span> methods.</p>
+ <p>May be <span class="Constant">nil</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td colspan="2"><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td colspan="2"><p>None.</p></td>
+ </tr>
+ </table>
+ <p>If the delegate object is changed, the old delegate object will no longer receive any messages that it may be expecting as a result of pending read or write operations that it initiated. To ensure there are no pending read or write operations, the delegate object can invoke <a class="Function" href="#api_cansafelysetdelegate">-canSafelySetDelegate:</a>.</p>
+
+ <a name="api_cansafelysetdelegate"></a>
+ <p class="Method_Heading">-canSafelySetDelegate</p>
+ <p>This message can be sent to determine whether a new delegate object needs to be made aware of pending read or write operations.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(BOOL)canSafelySetDelegate</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td>
+ <p><span class="Constant">YES</span>, if the receiver has any pending read or write operations.</p>
+ <p><span class="Constant">NO</span>, if the receiver has no pending read or write operations.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_conn"></a>
+ <h3>Connection Messages</h3>
+ <p><a class="Function" href="#api_connecthostporterror">-connectToHost:onPort:error:</a> <a class="Function" href="#api_acceptporterror">-acceptOnPort:error:</a> <a class="Function" href="#api_acceptaddressporterror">-acceptOnAddress:port:error:</a> <a class="Function" href="#api_isconnected">-isConnected</a> <a class="Function" href="#api_disconnect">-disconnect</a> <a class="Function" href="#api_disconnectafterwriting">-disconnectAfterWriting</a> <a class="Function" href="#api_connectedhost">-connectedHost</a> <a class="Function" href="#api_connectedport">-connectedPort</a> <a class="Function" href="#api_localhost">-localHost</a> <a class="Function" href="#api_localport">-localPort</a></p>
+ <a name="api_connecthostporterror"></a>
+ <p class="Method_Heading">-connectToHost:onPort:error:</p>
+ <p>This message establishes an outgoing connection from the receiver.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)err</p></td>
+ </tr>
+ <tr>
+ <th rowspan="3"><p>Arguments</p></th>
+ <td class="Argument"><p>hostname</p></td>
+ <td>
+ <p>A DNS name or IP address to which the receiver should connect. Both IPv4 and IPv6 addresses are supported.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>port</p></th>
+ <td>
+ <p>A port number to which the receiver should connect.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>err</p></td>
+ <td>
+ <p>The address of an <span class="Class">NSError</span> object pointer. In the event of an error, the pointer will be set to the <span class="Class">NSError</span> object describing the error.</p>
+ <p>The sender may pass <span class="Constant">nil</span> if it does not wish to retrieve any <span class="Class">NSError</span> object.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td colspan="2">
+ <p>Returns <span class="Constant">YES</span> if the connection is successful.</p>
+ <p>This does not indicate that the socket is ready for use. The socket is only ready when the <a class="Function" href="#api_sockdidconnecthostport">-onSocket:didConnectToHost:port:</a> delegate method is called.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td colspan="2">
+ <p>Returns <span class="Constant">NO</span> and an <span class="Class">NSError</span> object under the following conditions:</p>
+ <ul>
+ <li><span class="Argument">hostname</span> is not a valid address, or there is no such address.</li>
+ <li>The socket cannot be created, or cannot connect to the address.</li>
+ </ul>
+ <p>Returns <span class="Constant">YES</span> and calls <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> under the following conditions:</p>
+ <ul>
+ <li>The read and write streams could not be attached or opened.</li>
+ </ul>
+ <p>Raises an <a class="Constant" href="#api_sockexception">AsyncSocketException</a> if the socket is already connected or accepting connections, or if no delegate has been set.</p>
+ </td>
+ </tr>
+ </table>
+ <p>If the receiver returns <span class="Constant">YES</span>, it will continue to establish a connection. When the connection is successfully established, or fails to be established, the receiver will send an appropriate message to its delegate object.</p>
+ <p>Read and write operations may be queued before the connection is successfully established. They will be executed after the connection is complete.</p>
+
+ <a name="api_acceptporterror"></a>
+ <p class="Method_Heading">-acceptOnPort:error:</p>
+ <p>This message establishes the receiver as a listen socket that will accept incoming connections.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(BOOL)acceptOnPort:(UInt16)port error:(NSError **)err</p></td>
+ </tr>
+ <tr>
+ <th rowspan="2"><p>Arguments</p></th>
+ <td class="Argument"><p>port</p></td>
+ <td>
+ <p>A port number at which the receiver should accept connections.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>err</p></td>
+ <td>
+ <p>The address of an <span class="Class">NSError</span> object pointer. In the event of an error, the pointer will be set to the <span class="Class">NSError</span> object describing the error.</p>
+ <p>The sender may pass <span class="Constant">nil</span> if it does not wish to retrieve any <span class="Class">NSError</span> object.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td colspan="2">
+ <p>Returns <span class="Constant">YES</span> if the receiver is successfully accepting connections at the specified port.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td colspan="2">
+ <p>Returns <span class="Constant">NO</span> and an <span class="Class">NSError</span> object if the socket cannot be created, or cannot accept connections on the specified port.</p>
+ <p>Raises an <a class="Constant" href="#api_sockexception">AsyncSocketException</a> if the socket is already connected or accepting connections, or if no delegate has been set.</p>
+ </td>
+ </tr>
+ </table>
+ <p>The receiver establishes a listen socket with the <span class="Constant">SO_REUSEADDR</span> option set.</p>
+ <p>In the event of a connection from a remote socket, the receiver will create a new <span class="Class">AsyncSocket</span> instance. The new instance will have the same delegate object as the receiver, and will attempt to complete the connection to the remote socket.</p>
+ <p>There are three possible outcomes of the new instance’s attempt. First, the attempt could succeed. Second, the attempt could fail because a local socket could not be created. Third, the attempt could fail because of another issue.</p>
+ <p>If successful, the receiver will send <a class="Function" href="#api_sockdidacceptnewsock">-onSocket:didAcceptNewSocket:</a> and <a class="Function" href="#api_sockwantsrunloopnewsock">-onSocket:wantsRunLoopForNewSocket:</a> to its delegate object. At this point the delegate object can change the new instance’s delegate object or assign a run-loop. After the delegate methods return, the new instance will send <a class="Function" href="#api_sockdidconnecthostport">-onSocket:didConnectToHost:port:</a> to its delegate object.</p>
+ <p>If unsuccessful because a local socket could not be created, the new instance will be silently destroyed, and the receiver will continue to accept connections.</p>
+ <p>If unsuccessful for some other reason, the receiver will send <a class="Function" href="#api_sockdidacceptnewsock">-onSocket:didAcceptNewSocket:</a> and <a class="Function" href="#api_sockwantsrunloopnewsock">-onSocket:wantsRunLoopForNewSocket:</a> to its delegate object. After the delegate method returns, the new instance will send <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> to its delegate with details about the failure condition.</p>
+
+ <a name="api_acceptaddressporterror"></a>
+ <p class="Method_Heading">-acceptOnAddress:port:error:</p>
+ <p>This message establishes the receiver as a listen socket that will accept incoming connections on a particular host address and port.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(BOOL)acceptOnAddress:(NSString *)hostaddr port:(UInt16)port error:(NSError **)err</p></td>
+ </tr>
+ <tr>
+ <th rowspan="3"><p>Arguments</p></th>
+ <td class="Argument"><p>hostaddr</p></td>
+ <td>
+ <p>A host address at which the receiver should accept connections. The address should be an IPv4 or IPv6 address, such as “192.168.3.1” or “fe80::230:65ff:fe29:aa9d.”</p>
+ <p>If <span class="Constant">nil</span> or an empty string, the effect is the same as <a class="Function" href="#api_acceptporterror">-acceptOnPort:error:</a> message.
+ <tr>
+ <td class="Argument"><p>port</p></td>
+ <td>
+ <p>A port number at which the receiver should accept connections.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>err</p></td>
+ <td>
+ <p>The address of an <span class="Class">NSError</span> object pointer. In the event of an error, the pointer will be set to the <span class="Class">NSError</span> object describing the error.</p>
+ <p>The sender may pass <span class="Constant">nil</span> if it does not wish to retrieve any <span class="Class">NSError</span> object.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td colspan="2">
+ <p>Returns <span class="Constant">YES</span> if the receiver is successfully accepting connections at the specified port.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td colspan="2">
+ <p>Returns <span class="Constant">NO</span> and an <span class="Class">NSError</span> object if the socket cannot be created, or cannot accept connections on the specified address or port.</p>
+ <p>Raises an <a class="Constant" href="#api_sockexception">AsyncSocketException</a> if the socket is already connected or accepting connections, or if no delegate has been set.</p>
+ </td>
+ </tr>
+ </table>
+ <p>See <a class="Function" href="#api_acceptporterror">-acceptOnPort:error:</a> for more information.</p>
+
+ <a name="api_isconnected"></a>
+ <p class="Method_Heading">-isConnected</p>
+ <p>This message may be sent to determine whether the receiver is connected and capable of reading and writing.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>-(BOOL)isConnected</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td>
+ <p><span class="Constant">YES</span>, if the receiver is connected and able to send and receive data.</p>
+ <p><span class="Constant">NO</span>, if the receiver is not connected, accepting connections, or not able to send and receive data.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+ <p>If the input or output streams have reached <span class="Constant">EOF</span>, the receiver returns <span class="Constant">NO</span>. If the input or output streams are open, but in some other error state, the receiver returns <span class="Constant">YES</span>.</p>
+ <p>If the receiver is accepting incoming connections, it always returns <span class="Constant">NO</span>.</p>
+
+ <a name="api_disconnect"></a>
+ <p class="Method_Heading">-disconnect</p>
+ <p>This message immediately disconnects the receiver.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>-(void)disconnect</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+ <p>If the receiver was accepting incoming connections, it will no stop doing so. Any pending read or write operations are dropped.</p>
+ <p>After this method returns, the client application may send a <a class="Function" href="#api_connecthostporterror">-connectToHost:onPort:error:</a>, <a class="Function" href="#api_acceptporterror">-acceptOnPort:error:</a>, or <a class="Function" href="#api_acceptaddressporterror">-acceptOnAddress:port:error:</a> messages again.</p>
+
+ <a name="api_disconnectafterwriting"></a>
+ <p class="Method_Heading">-disconnectAfterWriting</p>
+ <p>This message will disconnect the receiver after all pending write operations are completed. Pending read operations will not prevent the receiver from disconnecting.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>-(void)disconnectAfterWriting</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+ <p>While the pending write operations are completing, the receiver will ignore any further read or write messages. Other messages may be sent as usual.</p>
+
+ <a name="api_connectedhost"></a>
+ <p class="Method_Heading">-connectedHost</p>
+ <p>This message returns the IP address of the connected remote socket as a string.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(NSString *)connectedHost</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td>
+ <p>If the receiver is connected, an IP address.</p>
+ <p>If the receiver is not connected, <span class="Constant">nil</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_connectedport"></a>
+ <p class="Method_Heading">-connectedPort</p>
+ <p>This message returns the port number of the connected remote socket.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>-(UInt16)connectedPort</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td>
+ <p>If the receiver is connected, a port number.</p>
+ <p>If the receiver is not connected, <span class="Constant">0</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_localhost"></a>
+ <p class="Method_Heading">-localHost</p>
+ <p>This method returns the local IP address of the receiver as a string.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>-(NSString *)localHost</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td>
+ <p>If the receiver is connected, an IP address.</p>
+ <p>If the receiver is not connected, <span class="Constant">nil</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+ <p>If the computer has more than one IP address, the one in use by the receiver will be returned. If the computer is behind a NAT, the returned IP address will be a LAN address, not useable outside the LAN.</p>
+
+ <a name="api_localport"></a>
+ <p class="Method_Heading">-localPort</p>
+ <p>This method returns the port number of the receiver.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>-(UInt16)localPort</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td>
+ <p>If the receiver is connected, a port number.</p>
+ <p>If the receiver is not connected, <span class="Constant">0</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+ <p>If the computer is behind a NAT, the returned port number will be a LAN address, not accurate outside the LAN.</p>
+
+ <a name="api_rw"></a>
+ <h3>Read and Write Messages</h3>
+ <p><a class="Function" href="#api_readdatalengthtimeouttag">-readDataToLength:withTimeout:tag:</a> <a class="Function" href="#api_readdatadatatimeouttag">-readDataToData:withTimeout:tag:</a> <a class="Function" href="#api_readdatatimeouttag">-readDataWithTimeout:tag:</a> <a class="Function" href="#api_writedatatimeouttag">-writeData:withTimeout:tag:</a> <a class="Function" href="#api_progressreadtagbytesdonetotal">-progressOfReadReturningTag:bytesDone:total:</a> <a class="Function" href="#api_progresswritetagbytesdonetotal">-progressOfWriteReturningTag:bytesDone:total:</a></p>
+
+ <a name="api_readdatalengthtimeouttag"></a>
+ <p class="Method_Heading">-readDataToLength:withTimeout:tag:</p>
+ <p>This message queues a read operation. The receiver will read a certain number of bytes from the socket.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag</p></td>
+ </tr>
+ <tr>
+ <th rowspan="3"><p>Arguments</p></th>
+ <td class="Argument"><p>length</p></td>
+ <td>
+ <p>Number of bytes that the receiver should read.</p>
+ <p>If <span class="Constant">0</span>, the receiver does nothing, and does not send <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> to its delegate.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>timeout</p></td>
+ <td>
+ <p>The number of seconds from the start of the read operation in which the operation must complete. If the operation takes longer than this interval, the operation times out.</p>
+ <p>If negative, the read operation will not time out.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>tag</p></td>
+ <td>
+ <p>An application-defined integer or pointer that will be sent as an argument to the <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> message sent to the delegate.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td colspan="2">
+ <p>The receiver will send <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> to the delegate object when the read operation has completed. The received data will be passed as an argument of that message.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td colspan="2">
+ <p>The receiver will send <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> to the delegate object under the following conditions:</p>
+ <ul>
+ <li>The read operation times out.</li>
+ <li>The receiver is disconnected from the remote socket.</li>
+ <li>Some other i/o error occurs.</li>
+ </ul>
+ <p>The <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> method may retrieve partially received data by sending <a class="Function" href="#api_readdatatimeouttag">-readDataWithTimeout:tag:</a> to the socket.</p>
+ </td>
+ </tr>
+ </table>
+ <p>When the bytes have been successfully received, the receiver will send <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> to its delegate object.</p>
+ <p>The read operation will be performed immediately if possible. If so, the receiver will send the message before returning from this method.</p>
+
+ <a name="api_readdatadatatimeouttag"></a>
+ <p class="Method_Heading">-readDataToData:withTimeout:tag:</p>
+ <p>This message queues a read operation. The receiver will read bytes until (and including) a sequence of bytes passed in the <span class="Argument">data</span> argument. That sequence acts as a separator or delimiter.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag</p></td>
+ </tr>
+ <tr>
+ <th rowspan="3"><p>Arguments</p></th>
+ <td class="Argument"><p>data</p></td>
+ <td>
+ <p>A sequence of bytes that mark the end of the read operation.</p>
+ <p>If <span class="Constant">nil</span> or empty, the receiver does nothing, and does not send <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> to its delegate.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>timeout</p></th>
+ <td>
+ <p>The number of seconds from the start of the read operation in which the operation must complete. If the operation takes longer than this interval, the operation times out.</p>
+ <p>If negative, the read operation will not time out.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>tag</p></th>
+ <td>
+ <p>An application-defined integer or pointer that will be sent as an argument to the <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> message sent to the delegate.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td colspan="2">
+ <p>The receiver will send <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> to the delegate object when the read operation has completed. The received data will be passed as an argument of that message. It will include the delimiter.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td colspan="2">
+ <p>The receiver will send <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> to the delegate object under the following conditions:</p>
+ <ul>
+ <li>The read operation times out.</li>
+ <li>The receiver is disconnected from the remote socket.</li>
+ <li>Some other i/o error occurs.</li>
+ </ul>
+ <p>The <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> method may retrieve partially received data by sending <a class="Function" href="#api_readdatatimeouttag">-readDataWithTimeout:tag:</a> to the socket.</p>
+ </td>
+ </tr>
+ </table>
+ <p>When the bytes have been successfully received, the receiver will send <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> to its delegate object, passing all the received bytes as a parameter, including the delimiting sequence.</p>
+ <p>The read operation will be performed immediately if possible. If so, the receiver will send the message before returning from this method.</p>
+ <p>Note that this method is not character-set aware. If a character should happen to be encoded to a sequence of bytes that matches the delimiting sequence, the read operation can prematurely end.</p>
+
+ <a name="api_readdatatimeouttag"></a>
+ <p class="Method_Heading">-readDataWithTimeout:tag:</p>
+ <p>This message queues a read operation. The receiver will retrieve the first available bytes.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag</p></td>
+ </tr>
+ <tr>
+ <th rowspan="2"><p>Arguments</p></th>
+ <td class="Argument"><p>timeout</p></td>
+ <td>
+ <p>
+ The number of seconds from the start of the read operation in which the operation must complete.
+ If the operation takes longer than this interval, the operation times out.
+ </p>
+ <p>If negative, the read operation will not time out.</p>
+ </td>
+ </tr>
+ </tr>
+ <tr>
+ <td class="Argument"><p>tag</p></td>
+ <td>
+ <p>An application-defined integer or pointer that will be sent as an argument to the <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> message sent to the delegate.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td colspan="2">
+ <p>
+ The receiver will send <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> to its delegate object
+ when the read operation has completed. The received data, if any, will be passed as an argument.
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td colspan="2">
+ <p>An error will occur under these conditions:</p>
+ <ul>
+ <li>The read operation times out.</li>
+ <li>The receiver is disconnected from the remote socket.</li>
+ <li>Some other i/o error occurs.</li>
+ </ul>
+ </td>
+ </tr>
+ </table>
+ <p>
+ When the bytes have been successfully retrieved, the receiver will then send <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a>
+ to its delegate object, passing all the received bytes as an argument.
+ </p>
+
+ <a name="api_writedatatimeouttag"></a>
+ <p class="Method_Heading">-writeData:withTimeout:tag:</p>
+ <p>This message queues a write operation. The receiver will write an <span class="Class">NSData</span> object to the socket.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag</p></td>
+ </tr>
+ <tr>
+ <th rowspan="3"><p>Arguments</p></th>
+ <td class="Argument"><p>data</p></td>
+ <td>
+ <p>The data that should be written to the remote socket.</p>
+ <p>If <span class="Constant">nil</span>, the receiver does nothing, and does not send <a class="Function" href="#api_sockdidwritedatatag">-onSocket:didWriteDataWithTag:</a> to its delegate.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>timeout</p></td>
+ <td>
+ <p>The number of seconds from the start of the write operation in which the operation must complete. If the operation takes longer than this interval, the operation times out.</p>
+ <p>If negative, the write operation will not time out.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>tag</p></td>
+ <td>
+ <p>An application-defined integer or pointer that will be sent as an argument to the <a class="Function" href="#api_sockdidwritedatatag">-onSocket:didWriteDataWithTag:</a> message sent to the delegate.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td colspan="2">
+ <p>None. The receiver will send <a class="Function" href="#api_sockdidwritedatatag">-onSocket:didWriteDataWithTag:</a> to the delegate object when the write operation has completed.
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td colspan="2">
+ <p>The receiver will send <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> to the delegate object under the following conditions:</p>
+ <ul>
+ <li>The write operation times out.</li>
+ <li>The receiver is disconnected from the remote socket.</li>
+ <li>Some other i/o error occurs.</li>
+ </ul>
+ </td>
+ </tr>
+ </table>
+ <p>When the bytes have been successfully sent, the receiver will send <a class="Function" href="#api_sockdidwritedatatag">-onSocket:didWriteData:withTag:</a> to its delegate object.</p>
+ <p>The write operation will be performed immediately if possible. If so, the receiver will send the message before returning from this method.</p>
+
+ <a name="api_progressreadtagbytesdonetotal"></a>
+ <p class="Method_Heading">-progressOfReadReturningTag:bytesDone:total:</p>
+ <p>This message can be sent to determine the progress of the current read operation.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total</p></td>
+ </tr>
+ <tr>
+ <th rowspan="3"><p>Arguments</p></th>
+ <td class="Argument"><p>tag</p></td>
+ <td>
+ <p>The address of a variable. When this method returns, the variable will contain the tag of the current read operation. If there is no current read operation, the variable will not be changed.</p>
+ <p>The sender may pass <span class="Constant">NULL</span> if it does not wish to retrieve the tag.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>done</p></td>
+ <td>
+ <p>The address of a variable. When this method returns, the variable will contain the number of bytes that the current read operation has read. If there is no current read operation, the variable will not be changed.</p>
+ <p>The sender may pass <span class="Constant">NULL</span> if it does not wish to retrieve the number of bytes read.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>total</p></td>
+ <td>
+ <p>The address of a variable. When this method returns, the variable will contain the total number of bytes that the current read operation expects to read. This value is only meaningful if the current read operation is a “read-to-length” operation. If there is no current read operation, the variable will not be changed.</p>
+ <p>The sender may pass <span class="Constant">NULL</span> if it does not wish to retrieve the total number of bytes being read.</p>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td colspan="2">
+ <p>A decimal value.</p>
+ <ul>
+ <li><span class="Constant">NaN</span> indicates that there is no read operation currently being executed. Use the <span class="Function">isnan()</span> function to test for this return value.</li>
+ <li><span class="Constant">0.0</span> indicates that no bytes have been read of the total.</li>
+ <li><span class="Constant">1.0</span> indicates that all bytes have been read of the total, or the total number of bytes that must be read is not known.</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td colspan="2"><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_progresswritetagbytesdonetotal"></a>
+ <p class="Method_Heading">-progressOfWriteReturningTag:bytesDone:total:</p>
+ <p>This message can be sent to determine the progress of the current write operation.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code" colspan="2"><p>-(float)progressOfWriteReturningTag(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total</p></td>
+ </tr>
+ <tr>
+ <th rowspan="3"><p>Arguments</p></th>
+ <td class="Argument"><p>tag</p></td>
+ <td>
+ <p>The address of a variable. When this method returns, the variable will contain the tag of the current read operation. If there is no current read operation, the variable will not be changed.</p>
+ <p>The sender may pass <span class="Constant">NULL</span> if it does not wish to retrieve the tag.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>done</p></td>
+ <td>
+ <p>The address of a variable. When this method returns, the variable will contain the number of bytes that have been sent. If there is no current read operation, the variable will not be changed.</p>
+ <p>The sender may pass <span class="Constant">NULL</span> if it does not wish to retrieve the number of bytes sent.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>total</p></td>
+ <td>
+ <p>The address of a variable. When this method returns, the variable will contain the total number of bytes that the current write operation is sending. If there is no current read operation, the variable will not be changed.</p>
+ <p>The sender may pass <span class="Constant">NULL</span> if it does not wish to retrieve the total number of bytes being sent.</p>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td colspan="2">
+ <p>A decimal value.</p>
+ <ul>
+ <li><span class="Constant">NaN</span> indicates that there is no write operation currently being executed. Use the <span class="Function">isnan()</span> function to test for this return value.</li>
+ <li><span class="Constant">0.0</span> indicates that no bytes have been sent of the total.</li>
+ <li><span class="Constant">1.0</span> indicates that all bytes have been sent of the total.</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td colspan="2"><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_delim"></a>
+ <h3>Delimiter Messages</h3>
+ <p>The results of these messages are commonly-used delimiters that can be passed as the <span class="Argument">data</span> argument of a <a class="Function" href="#api_readdatadatatimeouttag">-readDataToData:withTimeout:tag:</a> message.</p>
+ <p><a class="Function" href="#api_crlfdata">+CRLFData</a> <a class="Function" href="#api_crdata">+CRData</a> <a class="Function" href="#api_lfdata">+LFData</a> <a class="Function" href="#api_zerodata">+ZeroData</a></p>
+
+ <a name="api_crlfdata"></a>
+ <p class="Method_Heading">+CRLFData</p>
+ <p>This method returns the CRLF byte sequence, the line separator for DOS and Windows.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>+(NSData *)CRLFData</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td>
+ <p>An instance of <span class="Class">NSData</span> containing the bytes <span class="Constant">0D0A</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_crdata"></a>
+ <p class="Method_Heading">+CRData</p>
+ <p>This method returns the CR byte sequence, the line separator for Macintosh operating systems before Mac OS X.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>+(NSData *)CRData</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td>
+ <p>An instance of <span class="Class">NSData</span> containing the byte <span class="Constant">0D</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_lfdata"></a>
+ <p class="Method_Heading">+LFData</p>
+ <p>This method returns the LF byte sequence, the line separator for most Unix operating systems and Mac OS X.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>+(NSData *)LFData</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td>
+ <p>An instance of <span class="Class">NSData</span> containing the byte <span class="Constant">0A</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_zerodata"></a>
+ <p class="Method_Heading">+ZeroData</p>
+ <p>This method returns a zero byte, the delimiter for C strings.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>+(NSData *)ZeroData</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td>
+ <p>An instance of <span class="Class">NSData</span> containing the byte <span class="Constant">00</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+
+ <a name="api_debug"></a>
+ <h3>Debugging and Customization Messages</h3>
+ <p><a class="Function" href="#api_description">-description</a> <a class="Function" href="#api_getcfsocket">-getCFSocket</a> <a class="Function" href="#api_getcfreadstream">-getCFReadStream</a> <a class="Function" href="#api_getcfwritestream">-getCFWriteStream</a></p>
+
+ <a name="api_description"></a>
+ <p class="Method_Heading">-description</p>
+ <p>This message returns a description of the receiver suitable for debugging purposes.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>-(NSString *)description</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td><p>An string describing the receiver and its status.</p></td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+ <p>The returned description does not have any newline characters and includes:</p>
+ <ul>
+ <li>the receiver’s id/address and hash values.</li>
+ <li>a <span class="Class">CFSocket</span> reference number.</li>
+ <li>the local and remote socket addresses.</li>
+ <li>the number of queued read and write operations.</li>
+ <li>progress of current read and write operations.</li>
+ <li><span class="Class">CFReadStream</span> and <span class="Class">CFWriteStream</span> reference numbers.</li>
+ <li>the status of the read and write streams.</li>
+ <li>the connection status.</li>
+ </ul>
+
+ <a name="api_getcfsocket"></a>
+ <p class="Method_Heading">-getCFSocket</p>
+ <p>This message returns the internal <span class="Class">CFSocket</span> instance being using by the receiver, enabling access to the underlying Unix socket.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>-(CFSocketRef)getCFSocket</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td>
+ <p>The receiver’s <span class="Class">CFSocket</span> reference number, or <span class="Constant">NULL</span> if not connected or accepting connections.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+ <p>Do not close, read from, or write to the underlying socket.</p>
+
+ <a name="api_getcfreadstream"></a>
+ <p class="Method_Heading">-getCFReadStream</p>
+ <p>This method returns the internal <span class="Class">CFReadStream</span> instance being using by the receiver, enabling access to the underlying Carbon stream.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>-(CFReadStreamRef)getCFReadStream</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td>
+ <p>The receiver’s <span class="Class">CFReadStream</span> reference number, or <span class="Constant">NULL</span> if not connected or accepting connections.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+ <p>Do not close, read from, or write to the underlying stream.</p>
+
+ <a name="api_getcfwritestream"></a>
+ <p class="Method_Heading">-getCFWriteStream</p>
+ <p>This method returns the internal <span class="Class">CFWriteStream</span> instance being used by the receiver, enabling access to the underlying Carbon stream.</p>
+ <table>
+ <tr>
+ <th><p>Syntax</p></th>
+ <td class="Code"><p>-(CFWriteStreamRef)getCFWriteStream</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td>
+ <p>The receiver’s <span class="Class">CFWriteStream</span> reference number, or <span class="Constant">NULL</span> if not connected or accepting connections.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Errors</p></th>
+ <td><p>None.</p></td>
+ </tr>
+ </table>
+ <p>Do not close, read from, or write to the underlying stream.</p>
+
+ <a name="api_delegmeth"></a>
+ <h3>AsyncSocketDelegate Methods</h3>
+ <p>Delegate objects may implement these methods to respond to <span class="Class">AsyncSocket</span> messages.</p>
+ <p><a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> <a class="Function" href="#api_sockdiddisconnect">-onSocketDidDisconnect:</a> <a class="Function" href="#api_sockdidacceptnewsock">-onSocket:didAcceptNewSocket:</a> <a class="Function" href="#api_sockwantsrunloopnewsock">-onSocket:wantsRunLoopForNewSocket:</a> <a class="Function" href="#api_sockwillconnect">-onSocketWillConnect:</a> <a class="Function" href="#api_sockdidconnecthostport">-onSocket:didConnectToHost:port:</a> <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> <a class="Function" href="#api_sockdidwritedatatag">-onSocket:didWriteDataWithTag:</a></p>
+
+ <a name="api_sockwilldisconnecterror"></a>
+ <p class="Method_Heading">-onSocket:willDisconnectWithError:</p>
+ <p>In the event of an error, the socket is disconnected. The socket sends this message before disconnection.</p>
+ <table>
+ <tr>
+ <th><p>Prototype</p></th>
+ <td class="Code" colspan="2"><p>-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err</p></td>
+ </tr>
+ <tr>
+ <th rowspan="2"><p>Arguments</p></th>
+ <td class="Argument"><p>sock</p></td>
+ <td>
+ <p>The sending <span class="Class">AsyncSocket</span> instance.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>err</p></td>
+ <td>
+ <p>The error causing the socket to disconnect.</p>
+ <p>If <span class="Constant">nil</span>, then there is no error. This typically means the socket was cleanly closed by the remote client application.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Condition</p></th>
+ <td colspan="2">
+ <p>This message is sent:</p>
+ <ul>
+ <li>if there is an connection, time out, or other i/o error.</li>
+ <li>if the remote socket cleanly disconnects.</li>
+ <li>before the local socket is disconnected.</li>
+ </ul>
+ </td>
+ </tr>
+ </table>
+ <p>This message is primarily intended to give the receiver a chance to retrieve any remaining buffered data from the connection. The receiver may do this by sending <a class="Function" href="#api_readdatatimeouttag">-readDataWithTimeout:tag:</a> to the sender.</p>
+
+ <a name="api_sockdiddisconnect"></a>
+ <p class="Method_Heading">-onSocketDidDisconnect:</p>
+ <p>The socket sends this message is called after it has disconnected.</p>
+ <table>
+ <tr>
+ <th><p>Prototype</p></th>
+ <td class="Code" colspan="2"><p>-(void)onSocketDidDisconnect:(AsyncSocket *)sock</p></td>
+ </tr>
+ <tr>
+ <th><p>Arguments</p></th>
+ <td class="Argument"><p>sock</p></td>
+ <td>
+ <p>The sending <span class="Class">AsyncSocket</span> instance.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Condition</p></th>
+ <td colspan="2"><p>This message is sent after the sender disconnects for any reason.</p></td>
+ </tr>
+ </table>
+ <p>The receiver may safely release the sender in this method.</p>
+
+ <a name="api_sockdidacceptnewsock"></a>
+ <p class="Method_Heading">-onSocket:didAcceptNewSocket:</p>
+ <p>The socket sends this message to provide the receiver with a chance to save a new socket in an appropriate place.</p>
+ <table>
+ <tr>
+ <th><p>Prototype</p></th>
+ <td class="Code" colspan="2"><p>-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket</p></td>
+ </tr>
+ <tr>
+ <th rowspan="2"><p>Arguments</p></th>
+ <td class="Argument"><p>sock</p></td>
+ <td>
+ <p>The sending <span class="Class">AsyncSocket</span> instance. This instance will be accepting connections.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>newSocket</p></td>
+ <td>
+ <p>A new instance of <span class="Class">AsyncSocket</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Condition</p></th>
+ <td colspan="2">
+ <p>This method is sent after the sender accepts an incoming connection and creates a new instance of <span class="Class">AsyncSocket</span> to handle it.</p>
+ </td>
+ </tr>
+ </table>
+ <p>When this message is sent, <span class="Argument">newSocket</span> is not fully connected to the remote socket.</p>
+ <p>The receiver should assign and retain <span class="Argument">newSocket</span>, and may also set a more appropriate delegate.</p>
+
+ <a name="api_sockwantsrunloopnewsock"></a>
+ <p class="Method_Heading">-onSocket:wantsRunLoopForNewSocket:</p>
+ <p>The socket sends this message to determine which thread and run-loop the new socket and its delegate’s methods should operate on. Defaults to the current run-loop.</p>
+ <table>
+ <tr>
+ <th><p>Prototype</p></th>
+ <td class="Code" colspan="2"><p>-(NSRunLoop *)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket</p></td>
+ </tr>
+ <tr>
+ <th rowspan="2"><p>Arguments</p></th>
+ <td class="Argument"><p>sock</p></td>
+ <td>
+ <p>The sending <span class="Class">AsyncSocket</span> instance. This instance will be accepting connections.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>newSocket</p></td>
+ <td>
+ <p>An instance of <span class="Class">AsyncSocket</span>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td colspan="2">
+ <p>The instance of <span class="Class">NSRunLoop</span> associated with a target thread.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Condition</p></th>
+ <td colspan="2">
+ <p>This method is sent after the sender accepts an incoming connection and sends the <a class="Function" href="#api_sockdidacceptnewsock">-onSocket:didAcceptNewSocket:</a> message.</p>
+ </td>
+ </tr>
+ </table>
+ <p>When this message is sent, <span class="Argument">newSocket</span> is not fully connected to the remote socket.</p>
+ <p>The receiver should return the instance of <span class="Class">NSRunLoop</span> associated with a target thread. All delegate messages from <span class="Argument">newSocket</span> will be sent in the context of that thread to that run-loop, all timers will run on that run-loop, and all processing will occur on that thread.</p>
+ <p>If the receiver does not implement this method, the sender will use the current thread and run-loop.</p>
+
+ <a name="api_sockwillconnect"></a>
+ <p class="Method_Heading">-onSocketWillConnect:</p>
+ <p>The socket sends this message when it is about to connect to a remote socket.</p>
+ <table>
+ <tr>
+ <th><p>Prototype</p></th>
+ <td class="Code" colspan="2"><p>-(BOOL)onSocketWillConnect:(AsyncSocket *)sock</p></td>
+ </tr>
+ <tr>
+ <th><p>Argument</p></th>
+ <td class="Argument"><p>sock</p></td>
+ <td><p>The sender.</p></td>
+ </tr>
+ <tr>
+ <th><p>Return Value</p></th>
+ <td colspan="2">
+ <p><span class="Constant">YES</span> if the socket should continue to connect to the remote socket. This is the default if this method is not implemented by the receiver.</p>
+ <p><span class="Constant">NO</span> to cancel the connection.</p>
+ </td>
+ </tr>
+ <th><p>Condition</p></th>
+ <td colspan="2">
+ <p>This message is sent before the sender attempts to connect to:</p>
+ <ul>
+ <li>the remote socket specified by a <a class="Function" href="#api_connecthostporterror">-connectToHost:onPort:error:</a> message.</li>
+ <li>the remote socket that initiated a connection to the port specified by an <a class="Function" href="#api_acceptporterror">-acceptOnPort:error:</a> message.
+ <li>the remote socket that initiated a connection to the address and port specified by an <a class="Function" href="#api_acceptaddressporterror">-acceptOnAddress:port:error:</a> message.</li>
+ </ul>
+ </td>
+ </tr>
+ </table>
+ <p>This message is primarily intended to give the receiver a chance to configure properties of the internal <span class="Class">CFReadStream</span> or <span class="Class">CFWriteStream</span> instances. The receiver should return <span class="Constant">YES</span> or <span class="Constant">NO</span>, indicating whether the sender should continue connecting.</p>
+ <p>If the receiver returns <span class="Constant">NO</span> and the connection attempt was initiated by <a class="Function" href="api_connecthostporterror">-connectToHost:onPort:error:</a>, that method will return <span class="Constant">NO</span> to its sender, along with an <a class="Constant" href="#api_sockdomain">AsyncSocketCanceledError</a> error object.</p>
+ <p>If the receiver returns <span class="Constant">NO</span> and the connection attempt was initiated by a remote socket, no error will be reported.</p>
+
+ <a name="api_sockdidconnecthostport"></a>
+ <p class="Method_Heading">-onSocket:didConnectToHost:port:</p>
+ <p>The socket sends this message when it is connected and ready for reading or writing.</p>
+ <table>
+ <tr>
+ <th><p>Prototype</p></th>
+ <td class="Code" colspan="2"><p>-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port</p></td>
+ </tr>
+ <tr>
+ <th rowspan="3"><p>Arguments</p></th>
+ <td class="Argument"><p>sock</p></td>
+ <td><p>The sender.</p></td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>host</p></td>
+ <td>
+ <p>The remote socket’s IPv4 or IPv6 address.</p>
+ <p>This may differ from the IP address or DNS name sent to the sender as the <span class="Argument">hostname</span> argument of <a class="Function" href="#api_connecthostporterror">-connectToHost:onPort:error:</a>.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>port</p></th>
+ <td>
+ <p>The remote socket’s port.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Condition</p></th>
+ <td colspan="2">
+ <p>This message is sent after the sender has successfully connected to:</p>
+ <ul>
+ <li>the remote socket specified by a <a class="Function" href="#api_connecthostporterror">-connectToHost:onPort:error:</a> message.</li>
+ <li>the remote socket that initiated a connection to the port specified by an <a class="Function" href="#api_acceptporterror">-acceptOnPort:error:</a> message.
+ <li>the remote socket that initiated a connection to the address and port specified by an <a class="Function" href="#api_acceptaddressporterror">-acceptOnAddress:port:error:</a> message.</li>
+ </ul>
+ </td>
+ </tr>
+ </table>
+ <p>The receiver may choose to disconnect the sender, or to queue a read or write operation.</p>
+ <p>The socket will perform any previously-queued read or write operations after the receiver returns from this method.</p>
+
+ <a name="api_sockdidreaddatatag"></a>
+ <p class="Method_Heading">-onSocket:didReadData:withTag:</p>
+ <p>The sender sends this message when it successfully completes a read operation. It may send this message before the invoked read method returns, or later.</p>
+ <table>
+ <tr>
+ <th><p>Prototype</p></th>
+ <td class="Code" colspan="2"><p>-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData*)data withTag:(long)tag</p></td>
+ </tr>
+ <tr>
+ <th rowspan="3"><p>Arguments</p></th>
+ <td class="Argument"><p>sock</p></td>
+ <td><p>The sender.</p></td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>data</p></td>
+ <td>
+ <p>The received data.</p>
+ </td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>tag</p></td>
+ <td>
+ <p>The <span class="Argument">tag</span> argument passed in the read message.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Condition</p></th>
+ <td colspan="2">
+ <p>This message is sent after the sender has successfully received the requested data.</p></td>
+ </tr>
+ </table>
+ <p>If a read operation is unsuccessful, the sender will send a <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> message instead.</p>
+ <p>The receiver should process the data and queue a read or write operation or disconnect as needed.</p>
+
+ <a name="api_sockdidwritedatatag"></a>
+ <p class="Method_Heading">-onSocket:didWriteDataWithTag:</p>
+ <p>The sender sends this message when a write operation has successfully completed. It may send this message before the invoked write method returns, or later.</p>
+ <table>
+ <tr>
+ <th><p>Prototype</p></th>
+ <td class="Code" colspan="2"><p>-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag</p></td>
+ </tr>
+ <tr>
+ <th rowspan="2"><p>Arguments</p></th>
+ <td class="Argument"><p>sock</p></td>
+ <td><p>The sender.</p></td>
+ </tr>
+ <tr>
+ <td class="Argument"><p>tag</p></td>
+ <td>
+ <p>The <span class="Argument">tag</span> argument passed in the write message.</p>
+ </td>
+ </tr>
+ <tr>
+ <th><p>Condition</p></th>
+ <td colspan="2">
+ <p>This message is sent after the sender has successfully sent the provided data.</p>
+ </td>
+ </tr>
+ </table>
+ <p>If a write operation is unsuccessful, the sender will send a <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> message instead.</p>
+ <p>The receiver should queue a read or write operation or disconnect as needed.</p>
+
+ <a name="api_err"></a>
+ <h3>Errors</h3>
+
+ <a name="api_sockexception"></a>
+ <p class="Method_Heading">AsyncSocketException</p>
+ <p>An instance of <span class="Class">AsyncSocket</span> raises this exception when it receives an <a class="Function" href="#api_acceptporterror">-acceptOnPort:error:</a>, <a class="Function" href="#api_acceptaddressporterror">-acceptOnAddress:port:error:</a>, or <a class="Function" href="#api_connecthostporterror">-connectToHost:onPort:error:</a> message, and:</p>
+ <ul>
+ <li>it is already connected or accepting connections.</li>
+ <li>it has not been assigned a delegate.</li>
+ </ul>
+
+ <a name="api_sockdomain"></a>
+ <p class="Method_Heading">AsyncSocketErrorDomain</p>
+ <p>This <span class="Class">NSError</span> domain includes the following <span class="Class">AsyncSocketError</span> codes:</p>
+ <ul>
+ <li><p><span class="Constant">AsyncSocketCanceledError</span> indicates that a socket’s <a class="Function" href="#api_sockwillconnect">-onSocketWillConnect:</a> delegate method has returned <span class="Constant">NO</span>. It is returned from a <a class="Function" href="#api_connecthostporterror">-connectToHost:onPort:error:</a> message.</p></li>
+ <li><p><span class="Constant">AsyncSocketCFSocketError</span> indicates an error in a <span class="Class">CFSocket</span> operation. Such an error can occur while accepting incoming connections or establishing outgoing connections, and can be caused by an illegal port number or an internal error. It is generally returned from a <a class="Function" href="#api_acceptporterror">-acceptOnPort:error:</a>, <a class="Function" href="#api_acceptaddressporterror">-acceptOnAddress:port:error:</a>, or <a class="Function" href="#api_connecthostporterror">-connectToHost:onPort:error:</a> messages.</p></li>
+ <li><p><span class="Constant">AsyncSocketReadTimeoutError</span> indicates that a read operation has timed out. It is passed to a socket’s delegate in a <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> message.</p></li>
+ <li><p><span class="Constant">AsyncSocketWriteTimeoutError</span> indicates that a write operation has timed out. It is passed to a socket’s delegate in a <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> message.</p></li>
+ <li><p><span class="Constant">AsyncSocketNoError</span>, indicating no error, is never used. It is defined for completeness’ sake.</p></li>
+ </ul>
+ <p>You may provide localized error messages for these codes in an <span class="Filename ">AsyncSocket.strings</span> strings file in the main bundle, using as keys “AsyncSocketNoError,” <i>etc.</i> Default English strings are provided.</p>
+
+ <a name="new"></a>
+ <h2>API Changes</h2>
+ <p>Changes since AsyncSocket 4.2:</p>
+ <ul>
+ <li><p>Per-socket <a href="#api_ud">user data</a> has been added.</p></li>
+ <li><p><span class="Function">-readDataWithTag:</span> has been replaced by <a class="Function" href="#api_readdatatimeouttag">-readDataWithTimeout:tag:</a>. This method will not return empty data via the <a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a> delegate method, but will instead hold off until data arrives. The new method can still be used to recover unread data from within <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a>.</p></li>
+ </ul>
+ <p>Changes since AsyncSocket 4.0:</p>
+ <ul>
+ <li><p>Two methods have been added to allow you to monitor the current <a href="#api_progressreadtagbytesdonetotal">read</a> and <a href="#api_progresswritetagbytesdonetotal">write</a> operation.</p></li>
+ <li><p>A <a href="#api_acceptaddressporterror">new method</a> allows the socket to only accept connections on one address.</p></li>
+ <li><p>A <a href="#api_sockwillconnect">new delegate method</a> has been added to allow the underlying streams and <span class="Class">CFSocket</span> to be customized.</p></li>
+ <li><p>An <a href="#api_sockdomain">error code</a> has been added to support that method.</p></li>
+ </ul>
+
+ <p>Changes since AsyncSocket 3.13:</p>
+ <ul>
+ <li><p>AsyncSocket now requires Mac OS X 10.4 or later.</p></li>
+ <li><p><a class="Function" href="#api_acceptporterror">-acceptOnPort:</a> and <a class="Function" href="#api_connecthostporterror">-connectToHost:onPort:</a> now take an extra error parameter. This may be set to <span class="Constant">nil</span> if you are not interested in this feature.</p></li>
+ <li><p><a class="Function" href="#api_acceptporterror">-acceptOnPort:</a> and <a class="Function" href="#api_connecthostporterror">-connectToHost:onPort:</a> also raise an <a href="#api_sockexception">exception</a> if used while the socket is already connected or accepting connections, instead of returning <span class="Constant">NO</span>.</p></li>
+ <li><p>The <span class="Argument">error</span> argument of <a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a> is now an <span class="Class">NSError</span> object instead of a <span class="Class">CFStreamError</span> object.</p></li>
+ <li><p>A <a class="Function" href="#api_sockwantsrunloopnewsock">new delegate method</a> has been added to handle the threading of incoming connections.</p></li>
+ <li><p><span class="Function">+addressFromString:</span> has been removed. Use <span class="Class">CFHost</span> or <span class="Class">NSHost</span> instead.</p></li>
+ <li><p>It is now possible to distinguish disconnections caused by a read timeout from those caused by a write timeout.</p></li>
+ <li><p>You must add <span class="Filename ">/System/Library/Frameworks/CoreServices.framework</span> to the project; this is necessary to convert certain CFStreamError domains to NSError domains.</p></li>
+ </ul>
+
+ <a name="idx"></a>
+ <h2>API Index</h2>
+ <ul>
+ <li><a class="Function" href="#api_acceptaddressporterror">-acceptOnAddress:port:error:</a></li>
+ <li><a class="Function" href="#api_acceptporterror">-acceptOnPort:error:</a></li>
+ <li><a class="Constant" href="#api_sockdomain">AsyncSocketCanceledError</a></li>
+ <li><a class="Constant" href="#api_sockdomain">AsyncSocketCFSocketError</a></li>
+ <li><a class="Class" href="#api_sockdomain">AsyncSocketError</a></li>
+ <li><a class="Constant" href="#api_sockdomain">AsyncSocketErrorDomain</a></li>
+ <li><a class="Constant" href="#api_sockexception">AsyncSocketException</a></li>
+ <li><a class="Constant" href="#api_sockdomain">AsyncSocketNoError</a></li>
+ <li><a class="Constant" href="#api_sockdomain">AsyncSocketReadTimeoutError</a></li>
+ <li><a class="Constant" href="#api_sockdomain">AsyncSocketWriteTimeoutError</a></li>
+ <li><a class="Function" href="#api_cansafelysetdelegate">-canSafelySetDelegate</a></li>
+ <li><a class="Function" href="#api_connectedhost">-connectedHost</a></li>
+ <li><a class="Function" href="#api_connectedport">-connectedPort</a></li>
+ <li><a class="Function" href="#api_connecthostporterror">-connectToHost:onPort:error:</a></li>
+ <li><a class="Function" href="#api_crdata">+CRData</a></li>
+ <li><a class="Function" href="#api_crlfdata">+CRLFData</a></li>
+ <li><a class="Function" href="#api_dealloc">-dealloc</a></li>
+ <li><a class="Function" href="#api_delegate">-delegate</a></li>
+ <li><a class="Function" href="#api_description">-description</a></li>
+ <li><a class="Function" href="#api_disconnect">-disconnect</a></li>
+ <li><a class="Function" href="#api_disconnectafterwriting">-disconnectAfterWriting</a></li>
+ <li><a class="Function" href="#api_getcfreadstream">-getCFReadStream</a></li>
+ <li><a class="Function" href="#api_getcfsocket">-getCFSocket</a></li>
+ <li><a class="Function" href="#api_getcfwritestream">-getCFWriteStream</a></li>
+ <li><a class="Function" href="#api_init">-init</a></li>
+ <li><a class="Function" href="#api_initdelegate">-initWithDelegate:</a></li>
+ <li><a class="Function" href="#api_initdelegateuserdata">-initWithDelegate:userData:</a></li>
+ <li><a class="Function" href="#api_isconnected">-isConnected</a></li>
+ <li><a class="Function" href="#api_lfdata">+LFData</a></li>
+ <li><a class="Function" href="#api_localhost">-localHost</a></li>
+ <li><a class="Function" href="#api_localport">-localPort</a></li>
+ <li><a class="Function" href="#api_sockdidacceptnewsock">-onSocket:didAcceptNewSocket:</a></li>
+ <li><a class="Function" href="#api_sockdidconnecthostport">-onSocket:didConnectToHost:port:</a></li>
+ <li><a class="Function" href="#api_sockdiddisconnect">-onSocketDidDisconnect:</a></li>
+ <li><a class="Function" href="#api_sockdidreaddatatag">-onSocket:didReadData:withTag:</a></li>
+ <li><a class="Function" href="#api_sockdidwritedatatag">-onSocket:didWriteDataWithTag:</a></li>
+ <li><a class="Function" href="#api_sockwantsrunloopnewsock">-onSocket:wantsRunLoopForNewSocket:</a></li>
+ <li><a class="Function" href="#api_sockwillconnect">-onSocketWillConnect:</a></li>
+ <li><a class="Function" href="#api_sockwilldisconnecterror">-onSocket:willDisconnectWithError:</a></li>
+ <li><a class="Function" href="#api_progressreadtagbytesdonetotal">-progressOfReadReturningTag:bytesDone:total:</a></li>
+ <li><a class="Function" href="#api_progresswritetagbytesdonetotal">-progressOfWriteReturningTag:bytesDone:total:</a></li>
+ <li><a class="Function" href="#api_readdatadatatimeouttag">-readDataToData:withTimeout:tag:</a></li>
+ <li><a class="Function" href="#api_readdatalengthtimeouttag">-readDataToLength:withTimeout:tag:</a></li>
+ <li><a class="Function" href="#api_readdatatimeouttag">-readDataWithTimeout:tag:</a></li>
+ <li><a class="Function" href="#api_setdelegate">-setDelegate:</a></li>
+ <li><a class="Function" href="#api_setuserdata">-setUserData:</a></li>
+ <li><a class="Function" href="#api_userdata">-userData</a></li>
+ <li><a class="Function" href="#api_writedatatimeouttag">-writeData:withTimeout:tag:</a></li>
+ <li><a class="Function" href="#api_zerodata">+ZeroData</a></li>
+ </ul>
+
+ <hr>
+ <center><cite>Author: Dustin Voss</cite></center>
+ </body>
+</html>
--- /dev/null
+//
+// AsyncSocket.h
+//
+// This class is in the public domain.
+// Originally created by Dustin Voss on Wed Jan 29 2003.
+// Updated and maintained by Deusty Designs and the Mac development community.
+//
+// http://code.google.com/p/cocoaasyncsocket/
+//
+
+#import <Foundation/Foundation.h>
+
+@class AsyncSocket;
+@class AsyncReadPacket;
+@class AsyncWritePacket;
+
+extern NSString *const AsyncSocketException;
+extern NSString *const AsyncSocketErrorDomain;
+
+enum AsyncSocketError
+{
+ AsyncSocketCFSocketError = kCFSocketError, // From CFSocketError enum.
+ AsyncSocketNoError = 0, // Never used.
+ AsyncSocketCanceledError, // onSocketWillConnect: returned NO.
+ AsyncSocketConnectTimeoutError,
+ AsyncSocketReadMaxedOutError, // Reached set maxLength without completing
+ AsyncSocketReadTimeoutError,
+ AsyncSocketWriteTimeoutError
+};
+typedef enum AsyncSocketError AsyncSocketError;
+
+@interface NSObject (AsyncSocketDelegate)
+
+/**
+ * In the event of an error, the socket is closed.
+ * You may call "unreadData" during this call-back to get the last bit of data off the socket.
+ * When connecting, this delegate method may be called
+ * before"onSocket:didAcceptNewSocket:" or "onSocket:didConnectToHost:".
+**/
+- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
+
+/**
+ * Called when a socket disconnects with or without error. If you want to release a socket after it disconnects,
+ * do so here. It is not safe to do that during "onSocket:willDisconnectWithError:".
+**/
+- (void)onSocketDidDisconnect:(AsyncSocket *)sock;
+
+/**
+ * Called when a socket accepts a connection. Another socket is spawned to handle it. The new socket will have
+ * the same delegate and will call "onSocket:didConnectToHost:port:".
+**/
+- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
+
+/**
+ * Called when a new socket is spawned to handle a connection. This method should return the run-loop of the
+ * thread on which the new socket and its delegate should operate. If omitted, [NSRunLoop currentRunLoop] is used.
+**/
+- (NSRunLoop *)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket;
+
+/**
+ * Called when a socket is about to connect. This method should return YES to continue, or NO to abort.
+ * If aborted, will result in AsyncSocketCanceledError.
+ *
+ * If the connectToHost:onPort:error: method was called, the delegate will be able to access and configure the
+ * CFReadStream and CFWriteStream as desired prior to connection.
+ *
+ * If the connectToAddress:error: method was called, the delegate will be able to access and configure the
+ * CFSocket and CFSocketNativeHandle (BSD socket) as desired prior to connection. You will be able to access and
+ * configure the CFReadStream and CFWriteStream in the onSocket:didConnectToHost:port: method.
+**/
+- (BOOL)onSocketWillConnect:(AsyncSocket *)sock;
+
+/**
+ * Called when a socket connects and is ready for reading and writing.
+ * The host parameter will be an IP address, not a DNS name.
+**/
+- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port;
+
+/**
+ * Called when a socket has completed reading the requested data into memory.
+ * Not called if there is an error.
+**/
+- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;
+
+/**
+ * Called when a socket has read in data, but has not yet completed the read.
+ * This would occur if using readToData: or readToLength: methods.
+ * It may be used to for things such as updating progress bars.
+**/
+- (void)onSocket:(AsyncSocket *)sock didReadPartialDataOfLength:(CFIndex)partialLength tag:(long)tag;
+
+/**
+ * Called when a socket has completed writing the requested data. Not called if there is an error.
+**/
+- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
+
+/**
+ * Called after the socket has completed SSL/TLS negotiation.
+ * This method is not called unless you use the provided startTLS method.
+**/
+- (void)onSocket:(AsyncSocket *)sock didSecure:(BOOL)flag;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface AsyncSocket : NSObject
+{
+ CFSocketRef theSocket4; // IPv4 accept or connect socket
+ CFSocketRef theSocket6; // IPv6 accept or connect socket
+ CFReadStreamRef theReadStream;
+ CFWriteStreamRef theWriteStream;
+
+ CFRunLoopSourceRef theSource4; // For theSocket4
+ CFRunLoopSourceRef theSource6; // For theSocket6
+ CFRunLoopRef theRunLoop;
+ CFSocketContext theContext;
+ NSArray *theRunLoopModes;
+
+ NSTimer *theConnectTimer;
+
+ NSMutableArray *theReadQueue;
+ AsyncReadPacket *theCurrentRead;
+ NSTimer *theReadTimer;
+ NSMutableData *partialReadBuffer;
+
+ NSMutableArray *theWriteQueue;
+ AsyncWritePacket *theCurrentWrite;
+ NSTimer *theWriteTimer;
+
+ id theDelegate;
+ UInt16 theFlags;
+
+ long theUserData;
+}
+
+- (id)init;
+- (id)initWithDelegate:(id)delegate;
+- (id)initWithDelegate:(id)delegate userData:(long)userData;
+
+/* String representation is long but has no "\n". */
+- (NSString *)description;
+
+/**
+ * Use "canSafelySetDelegate" to see if there is any pending business (reads and writes) with the current delegate
+ * before changing it. It is, of course, safe to change the delegate before connecting or accepting connections.
+**/
+- (id)delegate;
+- (BOOL)canSafelySetDelegate;
+- (void)setDelegate:(id)delegate;
+
+/* User data can be a long, or an id or void * cast to a long. */
+- (long)userData;
+- (void)setUserData:(long)userData;
+
+/* Don't use these to read or write. And don't close them, either! */
+- (CFSocketRef)getCFSocket;
+- (CFReadStreamRef)getCFReadStream;
+- (CFWriteStreamRef)getCFWriteStream;
+
+// Once one of the accept or connect methods are called, the AsyncSocket instance is locked in
+// and the other accept/connect methods can't be called without disconnecting the socket first.
+// If the attempt fails or times out, these methods either return NO or
+// call "onSocket:willDisconnectWithError:" and "onSockedDidDisconnect:".
+
+// When an incoming connection is accepted, AsyncSocket invokes several delegate methods.
+// These methods are (in chronological order):
+// 1. onSocket:didAcceptNewSocket:
+// 2. onSocket:wantsRunLoopForNewSocket:
+// 3. onSocketWillConnect:
+//
+// Your server code will need to retain the accepted socket (if you want to accept it).
+// The best place to do this is probably in the onSocket:didAcceptNewSocket: method.
+//
+// After the read and write streams have been setup for the newly accepted socket,
+// the onSocket:didConnectToHost:port: method will be called on the proper run loop.
+
+/**
+ * Tells the socket to begin listening and accepting connections on the given port.
+ * When a connection comes in, the AsyncSocket instance will call the various delegate methods (see above).
+ * The socket will listen on all available interfaces (e.g. wifi, ethernet, etc)
+**/
+- (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr;
+
+/**
+ * This method is the same as acceptOnPort:error: with the additional option
+ * of specifying which interface to listen on. So, for example, if you were writing code for a server that
+ * has multiple IP addresses, you could specify which address you wanted to listen on. Or you could use it
+ * to specify that the socket should only accept connections over ethernet, and not other interfaces such as wifi.
+ * You may also use the special strings "localhost" or "loopback" to specify that
+ * the socket only accept connections from the local machine.
+ *
+ * To accept connections on any interface pass nil, or simply use the acceptOnPort:error: method.
+**/
+- (BOOL)acceptOnAddress:(NSString *)hostaddr port:(UInt16)port error:(NSError **)errPtr;
+
+/**
+ * Connects to the given host and port.
+ * The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2")
+**/
+- (BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr;
+
+/**
+ * This method is the same as connectToHost:onPort:error: with an additional timeout option.
+ * To not time out use a negative time interval, or simply use the connectToHost:onPort:error: method.
+**/
+- (BOOL)connectToHost:(NSString *)hostname
+ onPort:(UInt16)port
+ withTimeout:(NSTimeInterval)timeout
+ error:(NSError **)errPtr;
+
+/**
+ * Connects to the given address, specified as a sockaddr structure wrapped in a NSData object.
+ * For example, a NSData object returned from NSNetservice's addresses method.
+ *
+ * If you have an existing struct sockaddr you can convert it to a NSData object like so:
+ * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
+ * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
+**/
+- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
+
+/**
+ * This method is the same as connectToAddress:error: with an additional timeout option.
+ * To not time out use a negative time interval, or simply use the connectToAddress:error: method.
+**/
+- (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr;
+
+/**
+ * Disconnects immediately. Any pending reads or writes are dropped.
+**/
+- (void)disconnect;
+
+/**
+ * Disconnects after all pending reads have completed.
+ * After calling this, the read and write methods will do nothing.
+ * The socket will disconnect even if there are still pending writes.
+**/
+- (void)disconnectAfterReading;
+
+/**
+ * Disconnects after all pending writes have completed.
+ * After calling this, the read and write methods will do nothing.
+ * The socket will disconnect even if there are still pending reads.
+**/
+- (void)disconnectAfterWriting;
+
+/**
+ * Disconnects after all pending reads and writes have completed.
+ * After calling this, the read and write methods will do nothing.
+**/
+- (void)disconnectAfterReadingAndWriting;
+
+/* Returns YES if the socket and streams are open, connected, and ready for reading and writing. */
+- (BOOL)isConnected;
+
+/**
+ * Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected.
+ * The host will be an IP address.
+**/
+- (NSString *)connectedHost;
+- (UInt16)connectedPort;
+
+- (NSString *)localHost;
+- (UInt16)localPort;
+
+- (BOOL)isIPv4;
+- (BOOL)isIPv6;
+
+// The readData and writeData methods won't block. To not time out, use a negative time interval.
+// If they time out, "onSocket:disconnectWithError:" is called. The tag is for your convenience.
+// You can use it as an array index, step number, state id, pointer, etc., just like the socket's user data.
+
+/**
+ * This will read a certain number of bytes into memory, and call the delegate method when those bytes have been read.
+ * If there is an error, partially read data is lost.
+ * If the length is 0, this method does nothing and the delegate is not called.
+**/
+- (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
+
+/**
+ * This reads bytes until (and including) the passed "data" parameter, which acts as a separator.
+ * The bytes and the separator are returned by the delegate method.
+ *
+ * If you pass nil or zero-length data as the "data" parameter,
+ * the method will do nothing, and the delegate will not be called.
+ *
+ * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter.
+ * Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for
+ * a character, the read will prematurely end.
+**/
+- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
+
+/**
+ * Same as readDataToData:withTimeout:tag, with the additional restriction that the amount of data read
+ * may not surpass the given maxLength (specified in bytes).
+ *
+ * If you pass a maxLength parameter that is less than the length of the data parameter,
+ * the method will do nothing, and the delegate will not be called.
+ *
+ * If the max length is surpassed, it is treated the same as a timeout - the socket is closed.
+ *
+ * Pass -1 as maxLength if no length restriction is desired, or simply use the readDataToData:withTimeout:tag method.
+**/
+- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag;
+
+/**
+ * Reads the first available bytes that become available on the socket.
+**/
+- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag;
+
+/**
+ * Writes data to the socket, and calls the delegate when finished.
+ *
+ * If you pass in nil or zero-length data, this method does nothing and the delegate will not be called.
+**/
+- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
+
+/**
+ * Returns progress of current read or write, from 0.0 to 1.0, or NaN if no read/write (use isnan() to check).
+ * "tag", "done" and "total" will be filled in if they aren't NULL.
+**/
+- (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total;
+- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total;
+
+/**
+ * Secures the connection using SSL/TLS.
+ *
+ * This method may be called at any time, and the TLS handshake will occur after all pending reads and writes
+ * are finished. This allows one the option of sending a protocol dependent StartTLS message, and queuing
+ * the upgrade to TLS at the same time, without having to wait for the write to finish.
+ * Any reads or writes scheduled after this method is called will occur over the secured connection.
+ *
+ * The possible keys and values for the TLS settings are well documented.
+ * Some possible keys are:
+ * - kCFStreamSSLLevel
+ * - kCFStreamSSLAllowsExpiredCertificates
+ * - kCFStreamSSLAllowsExpiredRoots
+ * - kCFStreamSSLAllowsAnyRoot
+ * - kCFStreamSSLValidatesCertificateChain
+ * - kCFStreamSSLPeerName
+ * - kCFStreamSSLCertificates
+ * - kCFStreamSSLIsServer
+ *
+ * Please refer to Apple's documentation for associated values, as well as other possible keys.
+ *
+ * If you pass in nil or an empty dictionary, this method does nothing and the delegate will not be called.
+**/
+- (void)startTLS:(NSDictionary *)tlsSettings;
+
+/**
+ * For handling readDataToData requests, data is necessarily read from the socket in small increments.
+ * The performance can be much improved by allowing AsyncSocket to read larger chunks at a time and
+ * store any overflow in a small internal buffer.
+ * This is termed pre-buffering, as some data may be read for you before you ask for it.
+ * If you use readDataToData a lot, enabling pre-buffering will result in better performance, especially on the iPhone.
+ *
+ * The default pre-buffering state is controlled by the DEFAULT_PREBUFFERING definition.
+ * It is highly recommended one leave this set to YES.
+ *
+ * This method exists in case pre-buffering needs to be disabled by default for some reason.
+ * In that case, this method exists to allow one to easily enable pre-buffering when ready.
+**/
+- (void)enablePreBuffering;
+
+/**
+ * When you create an AsyncSocket, it is added to the runloop of the current thread.
+ * So for manually created sockets, it is easiest to simply create the socket on the thread you intend to use it.
+ *
+ * If a new socket is accepted, the delegate method onSocket:wantsRunLoopForNewSocket: is called to
+ * allow you to place the socket on a separate thread. This works best in conjunction with a thread pool design.
+ *
+ * If, however, you need to move the socket to a separate thread at a later time, this
+ * method may be used to accomplish the task.
+ *
+ * This method must be called from the thread/runloop the socket is currently running on.
+ *
+ * Note: After calling this method, all further method calls to this object should be done from the given runloop.
+ * Also, all delegate calls will be sent on the given runloop.
+**/
+- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop;
+
+/**
+ * Allows you to configure which run loop modes the socket uses.
+ * The default set of run loop modes is NSDefaultRunLoopMode.
+ *
+ * If you'd like your socket to continue operation during other modes, you may want to add modes such as
+ * NSModalPanelRunLoopMode or NSEventTrackingRunLoopMode. Or you may simply want to use NSRunLoopCommonModes.
+ *
+ * Accepted sockets will automatically inherit the same run loop modes as the listening socket.
+ *
+ * Note: NSRunLoopCommonModes is defined in 10.5. For previous versions one can use kCFRunLoopCommonModes.
+**/
+- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;
+
+/**
+ * Returns the current run loop modes the AsyncSocket instance is operating in.
+ * The default set of run loop modes is NSDefaultRunLoopMode.
+**/
+- (NSArray *)runLoopModes;
+
+/**
+ * In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read
+ * any data that's left on the socket.
+**/
+- (NSData *)unreadData;
+
+/* A few common line separators, for use with the readDataToData:... methods. */
++ (NSData *)CRLFData; // 0x0D0A
++ (NSData *)CRData; // 0x0D
++ (NSData *)LFData; // 0x0A
++ (NSData *)ZeroData; // 0x00
+
+@end
--- /dev/null
+//
+// AsyncSocket.m
+//
+// This class is in the public domain.
+// Originally created by Dustin Voss on Wed Jan 29 2003.
+// Updated and maintained by Deusty Designs and the Mac development community.
+//
+// http://code.google.com/p/cocoaasyncsocket/
+//
+
+#import "AsyncSocket.h"
+#import <sys/socket.h>
+#import <netinet/in.h>
+#import <arpa/inet.h>
+#import <netdb.h>
+
+#if TARGET_OS_IPHONE
+// Note: You may need to add the CFNetwork Framework to your project
+#import <CFNetwork/CFNetwork.h>
+#endif
+
+#pragma mark Declarations
+
+#define DEFAULT_PREBUFFERING YES // Whether pre-buffering is enabled by default
+
+#define READQUEUE_CAPACITY 5 // Initial capacity
+#define WRITEQUEUE_CAPACITY 5 // Initial capacity
+#define READALL_CHUNKSIZE 256 // Incremental increase in buffer size
+#define WRITE_CHUNKSIZE (1024 * 4) // Limit on size of each write pass
+
+NSString *const AsyncSocketException = @"AsyncSocketException";
+NSString *const AsyncSocketErrorDomain = @"AsyncSocketErrorDomain";
+
+// Mutex lock used by all instances of AsyncSocket, to protect getaddrinfo.
+// The man page says it is not thread-safe. (As of Mac OS X 10.4.7, and possibly earlier)
+static NSString *getaddrinfoLock = @"lock";
+
+enum AsyncSocketFlags
+{
+ kEnablePreBuffering = 1 << 0, // If set, pre-buffering is enabled
+ kDidPassConnectMethod = 1 << 1, // If set, disconnection results in delegate call
+ kDidCompleteOpenForRead = 1 << 2, // If set, open callback has been called for read stream
+ kDidCompleteOpenForWrite = 1 << 3, // If set, open callback has been called for write stream
+ kStartingTLS = 1 << 4, // If set, we're waiting for TLS negotiation to complete
+ kForbidReadsWrites = 1 << 5, // If set, no new reads or writes are allowed
+ kDisconnectAfterReads = 1 << 6, // If set, disconnect after no more reads are queued
+ kDisconnectAfterWrites = 1 << 7, // If set, disconnect after no more writes are queued
+ kClosingWithError = 1 << 8, // If set, the socket is being closed due to an error
+ kDequeueReadScheduled = 1 << 9, // If set, a maybeDequeueRead operation is already scheduled
+ kDequeueWriteScheduled = 1 << 10, // If set, a maybeDequeueWrite operation is already scheduled
+};
+
+@interface AsyncSocket (Private)
+
+// Connecting
+- (void)startConnectTimeout:(NSTimeInterval)timeout;
+- (void)endConnectTimeout;
+
+// Socket Implementation
+- (CFSocketRef) newAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr;
+- (BOOL) createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
+- (BOOL) attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr;
+- (BOOL) configureSocketAndReturnError:(NSError **)errPtr;
+- (BOOL) connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
+- (void) doAcceptWithSocket:(CFSocketNativeHandle)newSocket;
+- (void) doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)err;
+
+// Stream Implementation
+- (BOOL) createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr;
+- (BOOL) createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr;
+- (BOOL) attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr;
+- (BOOL) configureStreamsAndReturnError:(NSError **)errPtr;
+- (BOOL) openStreamsAndReturnError:(NSError **)errPtr;
+- (void) doStreamOpen;
+- (BOOL) setSocketFromStreamsAndReturnError:(NSError **)errPtr;
+
+// Disconnect Implementation
+- (void) closeWithError:(NSError *)err;
+- (void) recoverUnreadData;
+- (void) emptyQueues;
+- (void) close;
+
+// Errors
+- (NSError *) getErrnoError;
+- (NSError *) getAbortError;
+- (NSError *) getStreamError;
+- (NSError *) getSocketError;
+- (NSError *) getConnectTimeoutError;
+- (NSError *) getReadMaxedOutError;
+- (NSError *) getReadTimeoutError;
+- (NSError *) getWriteTimeoutError;
+- (NSError *) errorFromCFStreamError:(CFStreamError)err;
+
+// Diagnostics
+- (BOOL) isSocketConnected;
+- (BOOL) areStreamsConnected;
+- (NSString *) connectedHost: (CFSocketRef)socket;
+- (UInt16) connectedPort: (CFSocketRef)socket;
+- (NSString *) localHost: (CFSocketRef)socket;
+- (UInt16) localPort: (CFSocketRef)socket;
+- (NSString *) addressHost: (CFDataRef)cfaddr;
+- (UInt16) addressPort: (CFDataRef)cfaddr;
+
+// Reading
+- (void) doBytesAvailable;
+- (void) completeCurrentRead;
+- (void) endCurrentRead;
+- (void) scheduleDequeueRead;
+- (void) maybeDequeueRead;
+- (void) doReadTimeout:(NSTimer *)timer;
+
+// Writing
+- (void) doSendBytes;
+- (void) completeCurrentWrite;
+- (void) endCurrentWrite;
+- (void) scheduleDequeueWrite;
+- (void) maybeDequeueWrite;
+- (void) maybeScheduleDisconnect;
+- (void) doWriteTimeout:(NSTimer *)timer;
+
+// Run Loop
+- (void) runLoopAddSource:(CFRunLoopSourceRef)source;
+- (void) runLoopRemoveSource:(CFRunLoopSourceRef)source;
+- (void) runLoopAddTimer:(NSTimer *)timer;
+- (void) runLoopRemoveTimer:(NSTimer *)timer;
+- (void) runLoopUnscheduleReadStream;
+- (void) runLoopUnscheduleWriteStream;
+
+// Security
+- (void)maybeStartTLS;
+- (void)onTLSStarted:(BOOL)flag;
+
+// Callbacks
+- (void) doCFCallback:(CFSocketCallBackType)type forSocket:(CFSocketRef)sock withAddress:(NSData *)address withData:(const void *)pData;
+- (void) doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream;
+- (void) doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream;
+
+@end
+
+static void MyCFSocketCallback (CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *);
+static void MyCFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo);
+static void MyCFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo);
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * The AsyncReadPacket encompasses the instructions for any given read.
+ * The content of a read packet allows the code to determine if we're:
+ * - reading to a certain length
+ * - reading to a certain separator
+ * - or simply reading the first chunk of available data
+**/
+@interface AsyncReadPacket : NSObject
+{
+ @public
+ NSMutableData *buffer;
+ CFIndex bytesDone;
+ NSTimeInterval timeout;
+ CFIndex maxLength;
+ long tag;
+ NSData *term;
+ BOOL readAllAvailableData;
+}
+- (id)initWithData:(NSMutableData *)d
+ timeout:(NSTimeInterval)t
+ tag:(long)i
+ readAllAvailable:(BOOL)a
+ terminator:(NSData *)e
+ maxLength:(CFIndex)m;
+
+- (unsigned)readLengthForTerm;
+
+- (unsigned)prebufferReadLengthForTerm;
+- (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes;
+@end
+
+@implementation AsyncReadPacket
+
+- (id)initWithData:(NSMutableData *)d
+ timeout:(NSTimeInterval)t
+ tag:(long)i
+ readAllAvailable:(BOOL)a
+ terminator:(NSData *)e
+ maxLength:(CFIndex)m
+{
+ if((self = [super init]))
+ {
+ buffer = [d retain];
+ timeout = t;
+ tag = i;
+ readAllAvailableData = a;
+ term = [e copy];
+ bytesDone = 0;
+ maxLength = m;
+ }
+ return self;
+}
+
+/**
+ * For read packets with a set terminator, returns the safe length of data that can be read
+ * without going over a terminator, or the maxLength.
+ *
+ * It is assumed the terminator has not already been read.
+**/
+- (unsigned)readLengthForTerm
+{
+ NSAssert(term != nil, @"Searching for term in data when there is no term.");
+
+ // What we're going to do is look for a partial sequence of the terminator at the end of the buffer.
+ // If a partial sequence occurs, then we must assume the next bytes to arrive will be the rest of the term,
+ // and we can only read that amount.
+ // Otherwise, we're safe to read the entire length of the term.
+
+ unsigned result = [term length];
+
+ // Shortcut when term is a single byte
+ if(result == 1) return result;
+
+ // i = index within buffer at which to check data
+ // j = length of term to check against
+
+ // Note: Beware of implicit casting rules
+ // This could give you -1: MAX(0, (0 - [term length] + 1));
+
+ CFIndex i = MAX(0, (CFIndex)(bytesDone - [term length] + 1));
+ CFIndex j = MIN([term length] - 1, bytesDone);
+
+ while(i < bytesDone)
+ {
+ const void *subBuffer = [buffer bytes] + i;
+
+ if(memcmp(subBuffer, [term bytes], j) == 0)
+ {
+ result = [term length] - j;
+ break;
+ }
+
+ i++;
+ j--;
+ }
+
+ if(maxLength > 0)
+ return MIN(result, (maxLength - bytesDone));
+ else
+ return result;
+}
+
+/**
+ * Assuming pre-buffering is enabled, returns the amount of data that can be read
+ * without going over the maxLength.
+**/
+- (unsigned)prebufferReadLengthForTerm
+{
+ if(maxLength > 0)
+ return MIN(READALL_CHUNKSIZE, (maxLength - bytesDone));
+ else
+ return READALL_CHUNKSIZE;
+}
+
+/**
+ * For read packets with a set terminator, scans the packet buffer for the term.
+ * It is assumed the terminator had not been fully read prior to the new bytes.
+ *
+ * If the term is found, the number of excess bytes after the term are returned.
+ * If the term is not found, this method will return -1.
+ *
+ * Note: A return value of zero means the term was found at the very end.
+**/
+- (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes
+{
+ NSAssert(term != nil, @"Searching for term in data when there is no term.");
+
+ // We try to start the search such that the first new byte read matches up with the last byte of the term.
+ // We continue searching forward after this until the term no longer fits into the buffer.
+
+ // Note: Beware of implicit casting rules
+ // This could give you -1: MAX(0, 1 - 1 - [term length] + 1);
+
+ CFIndex i = MAX(0, (CFIndex)(bytesDone - numBytes - [term length] + 1));
+
+ while(i + [term length] <= bytesDone)
+ {
+ const void *subBuffer = [buffer bytes] + i;
+
+ if(memcmp(subBuffer, [term bytes], [term length]) == 0)
+ {
+ return bytesDone - (i + [term length]);
+ }
+
+ i++;
+ }
+
+ return -1;
+}
+
+- (void)dealloc
+{
+ [buffer release];
+ [term release];
+ [super dealloc];
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * The AsyncWritePacket encompasses the instructions for any given write.
+**/
+@interface AsyncWritePacket : NSObject
+{
+ @public
+ NSData *buffer;
+ CFIndex bytesDone;
+ long tag;
+ NSTimeInterval timeout;
+}
+- (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i;
+@end
+
+@implementation AsyncWritePacket
+
+- (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i
+{
+ if((self = [super init]))
+ {
+ buffer = [d retain];
+ timeout = t;
+ tag = i;
+ bytesDone = 0;
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [buffer release];
+ [super dealloc];
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * The AsyncSpecialPacket encompasses special instructions for interruptions in the read/write queues.
+ * This class my be altered to support more than just TLS in the future.
+**/
+@interface AsyncSpecialPacket : NSObject
+{
+ @public
+ NSDictionary *tlsSettings;
+}
+- (id)initWithTLSSettings:(NSDictionary *)settings;
+@end
+
+@implementation AsyncSpecialPacket
+
+- (id)initWithTLSSettings:(NSDictionary *)settings
+{
+ if((self = [super init]))
+ {
+ tlsSettings = [settings copy];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [tlsSettings release];
+ [super dealloc];
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation AsyncSocket
+
+- (id)init
+{
+ return [self initWithDelegate:nil userData:0];
+}
+
+- (id)initWithDelegate:(id)delegate
+{
+ return [self initWithDelegate:delegate userData:0];
+}
+
+// Designated initializer.
+- (id)initWithDelegate:(id)delegate userData:(long)userData
+{
+ if((self = [super init]))
+ {
+ theFlags = DEFAULT_PREBUFFERING ? kEnablePreBuffering : 0x00;
+ theDelegate = delegate;
+ theUserData = userData;
+
+ theSocket4 = NULL;
+ theSource4 = NULL;
+ theSocket6 = NULL;
+ theSource6 = NULL;
+ theRunLoop = NULL;
+ theReadStream = NULL;
+ theWriteStream = NULL;
+
+ theConnectTimer = nil;
+
+ theReadQueue = [[NSMutableArray alloc] initWithCapacity:READQUEUE_CAPACITY];
+ theCurrentRead = nil;
+ theReadTimer = nil;
+
+ partialReadBuffer = [[NSMutableData alloc] initWithCapacity:READALL_CHUNKSIZE];
+
+ theWriteQueue = [[NSMutableArray alloc] initWithCapacity:WRITEQUEUE_CAPACITY];
+ theCurrentWrite = nil;
+ theWriteTimer = nil;
+
+ // Socket context
+ NSAssert(sizeof(CFSocketContext) == sizeof(CFStreamClientContext), @"CFSocketContext != CFStreamClientContext");
+ theContext.version = 0;
+ theContext.info = self;
+ theContext.retain = nil;
+ theContext.release = nil;
+ theContext.copyDescription = nil;
+
+ // Default run loop modes
+ theRunLoopModes = [[NSArray arrayWithObject:NSDefaultRunLoopMode] retain];
+ }
+ return self;
+}
+
+// The socket may been initialized in a connected state and auto-released, so this should close it down cleanly.
+- (void)dealloc
+{
+ [self close];
+ [theReadQueue release];
+ [theWriteQueue release];
+ [theRunLoopModes release];
+ [partialReadBuffer release];
+ [NSObject cancelPreviousPerformRequestsWithTarget:theDelegate selector:@selector(onSocketDidDisconnect:) object:self];
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+ [super dealloc];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Accessors
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (long)userData
+{
+ return theUserData;
+}
+
+- (void)setUserData:(long)userData
+{
+ theUserData = userData;
+}
+
+- (id)delegate
+{
+ return theDelegate;
+}
+
+- (void)setDelegate:(id)delegate
+{
+ theDelegate = delegate;
+}
+
+- (BOOL)canSafelySetDelegate
+{
+ return ([theReadQueue count] == 0 && [theWriteQueue count] == 0 && theCurrentRead == nil && theCurrentWrite == nil);
+}
+
+- (CFSocketRef)getCFSocket
+{
+ if(theSocket4)
+ return theSocket4;
+ else
+ return theSocket6;
+}
+
+- (CFReadStreamRef)getCFReadStream
+{
+ return theReadStream;
+}
+
+- (CFWriteStreamRef)getCFWriteStream
+{
+ return theWriteStream;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Progress
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total
+{
+ // Check to make sure we're actually reading something right now
+ if (!theCurrentRead) return NAN;
+
+ // It's only possible to know the progress of our read if we're reading to a certain length
+ // If we're reading to data, we of course have no idea when the data will arrive
+ // If we're reading to timeout, then we have no idea when the next chunk of data will arrive.
+ BOOL hasTotal = (theCurrentRead->readAllAvailableData == NO && theCurrentRead->term == nil);
+
+ CFIndex d = theCurrentRead->bytesDone;
+ CFIndex t = hasTotal ? [theCurrentRead->buffer length] : 0;
+ if (tag != NULL) *tag = theCurrentRead->tag;
+ if (done != NULL) *done = d;
+ if (total != NULL) *total = t;
+ float ratio = (float)d/(float)t;
+ return isnan(ratio) ? 1.0F : ratio; // 0 of 0 bytes is 100% done.
+}
+
+- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total
+{
+ if (!theCurrentWrite) return NAN;
+ CFIndex d = theCurrentWrite->bytesDone;
+ CFIndex t = [theCurrentWrite->buffer length];
+ if (tag != NULL) *tag = theCurrentWrite->tag;
+ if (done != NULL) *done = d;
+ if (total != NULL) *total = t;
+ return (float)d/(float)t;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Run Loop
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)runLoopAddSource:(CFRunLoopSourceRef)source
+{
+ unsigned i, count = [theRunLoopModes count];
+ for(i = 0; i < count; i++)
+ {
+ CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
+ CFRunLoopAddSource(theRunLoop, source, runLoopMode);
+ }
+}
+
+- (void)runLoopRemoveSource:(CFRunLoopSourceRef)source
+{
+ unsigned i, count = [theRunLoopModes count];
+ for(i = 0; i < count; i++)
+ {
+ CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
+ CFRunLoopRemoveSource(theRunLoop, source, runLoopMode);
+ }
+}
+
+- (void)runLoopAddTimer:(NSTimer *)timer
+{
+ unsigned i, count = [theRunLoopModes count];
+ for(i = 0; i < count; i++)
+ {
+ CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
+ CFRunLoopAddTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode);
+ }
+}
+
+- (void)runLoopRemoveTimer:(NSTimer *)timer
+{
+ unsigned i, count = [theRunLoopModes count];
+ for(i = 0; i < count; i++)
+ {
+ CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
+ CFRunLoopRemoveTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode);
+ }
+}
+
+- (void)runLoopUnscheduleReadStream
+{
+ unsigned i, count = [theRunLoopModes count];
+ for(i = 0; i < count; i++)
+ {
+ CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
+ CFReadStreamUnscheduleFromRunLoop(theReadStream, theRunLoop, runLoopMode);
+ }
+ CFReadStreamSetClient(theReadStream, kCFStreamEventNone, NULL, NULL);
+}
+
+- (void)runLoopUnscheduleWriteStream
+{
+ unsigned i, count = [theRunLoopModes count];
+ for(i = 0; i < count; i++)
+ {
+ CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
+ CFWriteStreamUnscheduleFromRunLoop(theWriteStream, theRunLoop, runLoopMode);
+ }
+ CFWriteStreamSetClient(theWriteStream, kCFStreamEventNone, NULL, NULL);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Configuration
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * See the header file for a full explanation of pre-buffering.
+**/
+- (void)enablePreBuffering
+{
+ theFlags |= kEnablePreBuffering;
+}
+
+/**
+ * See the header file for a full explanation of this method.
+**/
+- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop
+{
+ NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()),
+ @"moveToRunLoop must be called from within the current RunLoop!");
+
+ if(runLoop == nil)
+ {
+ return NO;
+ }
+ if(theRunLoop == [runLoop getCFRunLoop])
+ {
+ return YES;
+ }
+
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+ theFlags &= ~kDequeueReadScheduled;
+ theFlags &= ~kDequeueWriteScheduled;
+
+ if(theReadStream && theWriteStream)
+ {
+ [self runLoopUnscheduleReadStream];
+ [self runLoopUnscheduleWriteStream];
+ }
+
+ if(theSource4) [self runLoopRemoveSource:theSource4];
+ if(theSource6) [self runLoopRemoveSource:theSource6];
+
+ // We do not retain the timers - they get retained by the runloop when we add them as a source.
+ // Since we're about to remove them as a source, we retain now, and release again below.
+ [theReadTimer retain];
+ [theWriteTimer retain];
+
+ if(theReadTimer) [self runLoopRemoveTimer:theReadTimer];
+ if(theWriteTimer) [self runLoopRemoveTimer:theWriteTimer];
+
+ theRunLoop = [runLoop getCFRunLoop];
+
+ if(theReadTimer) [self runLoopAddTimer:theReadTimer];
+ if(theWriteTimer) [self runLoopAddTimer:theWriteTimer];
+
+ // Release timers since we retained them above
+ [theReadTimer release];
+ [theWriteTimer release];
+
+ if(theSource4) [self runLoopAddSource:theSource4];
+ if(theSource6) [self runLoopAddSource:theSource6];
+
+ if(theReadStream && theWriteStream)
+ {
+ if(![self attachStreamsToRunLoop:runLoop error:nil])
+ {
+ return NO;
+ }
+ }
+
+ [runLoop performSelector:@selector(maybeDequeueRead) target:self argument:nil order:0 modes:theRunLoopModes];
+ [runLoop performSelector:@selector(maybeDequeueWrite) target:self argument:nil order:0 modes:theRunLoopModes];
+ [runLoop performSelector:@selector(maybeScheduleDisconnect) target:self argument:nil order:0 modes:theRunLoopModes];
+
+ return YES;
+}
+
+/**
+ * See the header file for a full explanation of this method.
+**/
+- (BOOL)setRunLoopModes:(NSArray *)runLoopModes
+{
+ NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()),
+ @"setRunLoopModes must be called from within the current RunLoop!");
+
+ if([runLoopModes count] == 0)
+ {
+ return NO;
+ }
+ if([theRunLoopModes isEqualToArray:runLoopModes])
+ {
+ return YES;
+ }
+
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+ theFlags &= ~kDequeueReadScheduled;
+ theFlags &= ~kDequeueWriteScheduled;
+
+ if(theReadStream && theWriteStream)
+ {
+ [self runLoopUnscheduleReadStream];
+ [self runLoopUnscheduleWriteStream];
+ }
+
+ if(theSource4) [self runLoopRemoveSource:theSource4];
+ if(theSource6) [self runLoopRemoveSource:theSource6];
+
+ // We do not retain the timers - they get retained by the runloop when we add them as a source.
+ // Since we're about to remove them as a source, we retain now, and release again below.
+ [theReadTimer retain];
+ [theWriteTimer retain];
+
+ if(theReadTimer) [self runLoopRemoveTimer:theReadTimer];
+ if(theWriteTimer) [self runLoopRemoveTimer:theWriteTimer];
+
+ [theRunLoopModes release];
+ theRunLoopModes = [runLoopModes copy];
+
+ if(theReadTimer) [self runLoopAddTimer:theReadTimer];
+ if(theWriteTimer) [self runLoopAddTimer:theWriteTimer];
+
+ // Release timers since we retained them above
+ [theReadTimer release];
+ [theWriteTimer release];
+
+ if(theSource4) [self runLoopAddSource:theSource4];
+ if(theSource6) [self runLoopAddSource:theSource6];
+
+ if(theReadStream && theWriteStream)
+ {
+ // Note: theRunLoop variable is a CFRunLoop, and NSRunLoop is NOT toll-free bridged with CFRunLoop.
+ // So we cannot pass theRunLoop to the method below, which is expecting a NSRunLoop parameter.
+ // Instead we pass nil, which will result in the method properly using the current run loop.
+
+ if(![self attachStreamsToRunLoop:nil error:nil])
+ {
+ return NO;
+ }
+ }
+
+ [self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
+ [self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0 inModes:theRunLoopModes];
+ [self performSelector:@selector(maybeScheduleDisconnect) withObject:nil afterDelay:0 inModes:theRunLoopModes];
+
+ return YES;
+}
+
+- (NSArray *)runLoopModes
+{
+ return [[theRunLoopModes retain] autorelease];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Accepting
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr
+{
+ return [self acceptOnAddress:nil port:port error:errPtr];
+}
+
+/**
+ * To accept on a certain address, pass the address to accept on.
+ * To accept on any address, pass nil or an empty string.
+ * To accept only connections from localhost pass "localhost" or "loopback".
+**/
+- (BOOL)acceptOnAddress:(NSString *)hostaddr port:(UInt16)port error:(NSError **)errPtr
+{
+ if (theDelegate == NULL)
+ [NSException raise:AsyncSocketException format:@"Attempting to accept without a delegate. Set a delegate first."];
+
+ if (theSocket4 != NULL || theSocket6 != NULL)
+ [NSException raise:AsyncSocketException format:@"Attempting to accept while connected or accepting connections. Disconnect first."];
+
+ // Set up the listen sockaddr structs if needed.
+
+ NSData *address4 = nil, *address6 = nil;
+ if(hostaddr == nil || ([hostaddr length] == 0))
+ {
+ // Accept on ANY address
+ struct sockaddr_in nativeAddr4;
+ nativeAddr4.sin_len = sizeof(struct sockaddr_in);
+ nativeAddr4.sin_family = AF_INET;
+ nativeAddr4.sin_port = htons(port);
+ nativeAddr4.sin_addr.s_addr = htonl(INADDR_ANY);
+ memset(&(nativeAddr4.sin_zero), 0, sizeof(nativeAddr4.sin_zero));
+
+ struct sockaddr_in6 nativeAddr6;
+ nativeAddr6.sin6_len = sizeof(struct sockaddr_in6);
+ nativeAddr6.sin6_family = AF_INET6;
+ nativeAddr6.sin6_port = htons(port);
+ nativeAddr6.sin6_flowinfo = 0;
+ nativeAddr6.sin6_addr = in6addr_any;
+ nativeAddr6.sin6_scope_id = 0;
+
+ // Wrap the native address structures for CFSocketSetAddress.
+ address4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)];
+ address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
+ }
+ else if([hostaddr isEqualToString:@"localhost"] || [hostaddr isEqualToString:@"loopback"])
+ {
+ // Accept only on LOOPBACK address
+ struct sockaddr_in nativeAddr4;
+ nativeAddr4.sin_len = sizeof(struct sockaddr_in);
+ nativeAddr4.sin_family = AF_INET;
+ nativeAddr4.sin_port = htons(port);
+ nativeAddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ memset(&(nativeAddr4.sin_zero), 0, sizeof(nativeAddr4.sin_zero));
+
+ struct sockaddr_in6 nativeAddr6;
+ nativeAddr6.sin6_len = sizeof(struct sockaddr_in6);
+ nativeAddr6.sin6_family = AF_INET6;
+ nativeAddr6.sin6_port = htons(port);
+ nativeAddr6.sin6_flowinfo = 0;
+ nativeAddr6.sin6_addr = in6addr_loopback;
+ nativeAddr6.sin6_scope_id = 0;
+
+ // Wrap the native address structures for CFSocketSetAddress.
+ address4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)];
+ address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
+ }
+ else
+ {
+ NSString *portStr = [NSString stringWithFormat:@"%hu", port];
+
+ @synchronized (getaddrinfoLock)
+ {
+ struct addrinfo hints, *res, *res0;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_flags = AI_PASSIVE;
+
+ int error = getaddrinfo([hostaddr UTF8String], [portStr UTF8String], &hints, &res0);
+
+ if(error)
+ {
+ if(errPtr)
+ {
+ NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding];
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info];
+ }
+ }
+
+ for(res = res0; res; res = res->ai_next)
+ {
+ if(!address4 && (res->ai_family == AF_INET))
+ {
+ // Found IPv4 address
+ // Wrap the native address structures for CFSocketSetAddress.
+ address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
+ }
+ else if(!address6 && (res->ai_family == AF_INET6))
+ {
+ // Found IPv6 address
+ // Wrap the native address structures for CFSocketSetAddress.
+ address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
+ }
+ }
+ freeaddrinfo(res0);
+ }
+
+ if(!address4 && !address6) return NO;
+ }
+
+ // Create the sockets.
+
+ if (address4)
+ {
+ theSocket4 = [self newAcceptSocketForAddress:address4 error:errPtr];
+ if (theSocket4 == NULL) goto Failed;
+ }
+
+ if (address6)
+ {
+ theSocket6 = [self newAcceptSocketForAddress:address6 error:errPtr];
+
+ // Note: The iPhone doesn't currently support IPv6
+
+#if !TARGET_OS_IPHONE
+ if (theSocket6 == NULL) goto Failed;
+#endif
+ }
+
+ // Attach the sockets to the run loop so that callback methods work
+
+ [self attachSocketsToRunLoop:nil error:nil];
+
+ // Set the SO_REUSEADDR flags.
+
+ int reuseOn = 1;
+ if (theSocket4) setsockopt(CFSocketGetNative(theSocket4), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
+ if (theSocket6) setsockopt(CFSocketGetNative(theSocket6), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
+
+ // Set the local bindings which causes the sockets to start listening.
+
+ CFSocketError err;
+ if (theSocket4)
+ {
+ err = CFSocketSetAddress (theSocket4, (CFDataRef)address4);
+ if (err != kCFSocketSuccess) goto Failed;
+
+ //NSLog(@"theSocket4: %hu", [self localPort:theSocket4]);
+ }
+
+ if(port == 0 && theSocket4 && theSocket6)
+ {
+ // The user has passed in port 0, which means he wants to allow the kernel to choose the port for them
+ // However, the kernel will choose a different port for both theSocket4 and theSocket6
+ // So we grab the port the kernel choose for theSocket4, and set it as the port for theSocket6
+ UInt16 chosenPort = [self localPort:theSocket4];
+
+ struct sockaddr_in6 *pSockAddr6 = (struct sockaddr_in6 *)[address6 bytes];
+ pSockAddr6->sin6_port = htons(chosenPort);
+ }
+
+ if (theSocket6)
+ {
+ err = CFSocketSetAddress (theSocket6, (CFDataRef)address6);
+ if (err != kCFSocketSuccess) goto Failed;
+
+ //NSLog(@"theSocket6: %hu", [self localPort:theSocket6]);
+ }
+
+ theFlags |= kDidPassConnectMethod;
+ return YES;
+
+Failed:
+ if(errPtr) *errPtr = [self getSocketError];
+ if(theSocket4 != NULL)
+ {
+ CFSocketInvalidate(theSocket4);
+ CFRelease(theSocket4);
+ theSocket4 = NULL;
+ }
+ if(theSocket6 != NULL)
+ {
+ CFSocketInvalidate(theSocket6);
+ CFRelease(theSocket6);
+ theSocket6 = NULL;
+ }
+ return NO;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Connecting
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (BOOL)connectToHost:(NSString*)hostname onPort:(UInt16)port error:(NSError **)errPtr
+{
+ return [self connectToHost:hostname onPort:port withTimeout:-1 error:errPtr];
+}
+
+/**
+ * This method creates an initial CFReadStream and CFWriteStream to the given host on the given port.
+ * The connection is then opened, and the corresponding CFSocket will be extracted after the connection succeeds.
+ *
+ * Thus the delegate will have access to the CFReadStream and CFWriteStream prior to connection,
+ * specifically in the onSocketWillConnect: method.
+**/
+- (BOOL)connectToHost:(NSString *)hostname
+ onPort:(UInt16)port
+ withTimeout:(NSTimeInterval)timeout
+ error:(NSError **)errPtr
+{
+ if(theDelegate == NULL)
+ {
+ NSString *message = @"Attempting to connect without a delegate. Set a delegate first.";
+ [NSException raise:AsyncSocketException format:message];
+ }
+
+ if(theSocket4 != NULL || theSocket6 != NULL)
+ {
+ NSString *message = @"Attempting to connect while connected or accepting connections. Disconnect first.";
+ [NSException raise:AsyncSocketException format:message];
+ }
+
+ if(![self createStreamsToHost:hostname onPort:port error:errPtr]) goto Failed;
+ if(![self attachStreamsToRunLoop:nil error:errPtr]) goto Failed;
+ if(![self configureStreamsAndReturnError:errPtr]) goto Failed;
+ if(![self openStreamsAndReturnError:errPtr]) goto Failed;
+
+ [self startConnectTimeout:timeout];
+ theFlags |= kDidPassConnectMethod;
+
+ return YES;
+
+Failed:
+ [self close];
+ return NO;
+}
+
+- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr
+{
+ return [self connectToAddress:remoteAddr withTimeout:-1 error:errPtr];
+}
+
+/**
+ * This method creates an initial CFSocket to the given address.
+ * The connection is then opened, and the corresponding CFReadStream and CFWriteStream will be
+ * created from the low-level sockets after the connection succeeds.
+ *
+ * Thus the delegate will have access to the CFSocket and CFSocketNativeHandle (BSD socket) prior to connection,
+ * specifically in the onSocketWillConnect: method.
+ *
+ * Note: The NSData parameter is expected to be a sockaddr structure. For example, an NSData object returned from
+ * NSNetservice addresses method.
+ * If you have an existing struct sockaddr you can convert it to an NSData object like so:
+ * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
+ * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
+**/
+- (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr
+{
+ if (theDelegate == NULL)
+ {
+ NSString *message = @"Attempting to connect without a delegate. Set a delegate first.";
+ [NSException raise:AsyncSocketException format:message];
+ }
+
+ if (theSocket4 != NULL || theSocket6 != NULL)
+ {
+ NSString *message = @"Attempting to connect while connected or accepting connections. Disconnect first.";
+ [NSException raise:AsyncSocketException format:message];
+ }
+
+ if(![self createSocketForAddress:remoteAddr error:errPtr]) goto Failed;
+ if(![self attachSocketsToRunLoop:nil error:errPtr]) goto Failed;
+ if(![self configureSocketAndReturnError:errPtr]) goto Failed;
+ if(![self connectSocketToAddress:remoteAddr error:errPtr]) goto Failed;
+
+ [self startConnectTimeout:timeout];
+ theFlags |= kDidPassConnectMethod;
+
+ return YES;
+
+Failed:
+ [self close];
+ return NO;
+}
+
+- (void)startConnectTimeout:(NSTimeInterval)timeout
+{
+ if(timeout >= 0.0)
+ {
+ theConnectTimer = [NSTimer timerWithTimeInterval:timeout
+ target:self
+ selector:@selector(doConnectTimeout:)
+ userInfo:nil
+ repeats:NO];
+ [self runLoopAddTimer:theConnectTimer];
+ }
+}
+
+- (void)endConnectTimeout
+{
+ [theConnectTimer invalidate];
+ theConnectTimer = nil;
+}
+
+- (void)doConnectTimeout:(NSTimer *)timer
+{
+ [self endConnectTimeout];
+ [self closeWithError:[self getConnectTimeoutError]];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Socket Implementation
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Creates the accept sockets.
+ * Returns true if either IPv4 or IPv6 is created.
+ * If either is missing, an error is returned (even though the method may return true).
+**/
+- (CFSocketRef)newAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr
+{
+ struct sockaddr *pSockAddr = (struct sockaddr *)[addr bytes];
+ int addressFamily = pSockAddr->sa_family;
+
+ CFSocketRef theSocket = CFSocketCreate(kCFAllocatorDefault,
+ addressFamily,
+ SOCK_STREAM,
+ 0,
+ kCFSocketAcceptCallBack, // Callback flags
+ (CFSocketCallBack)&MyCFSocketCallback, // Callback method
+ &theContext);
+
+ if(theSocket == NULL)
+ {
+ if(errPtr) *errPtr = [self getSocketError];
+ }
+
+ return theSocket;
+}
+
+- (BOOL)createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr
+{
+ struct sockaddr *pSockAddr = (struct sockaddr *)[remoteAddr bytes];
+
+ if(pSockAddr->sa_family == AF_INET)
+ {
+ theSocket4 = CFSocketCreate(NULL, // Default allocator
+ PF_INET, // Protocol Family
+ SOCK_STREAM, // Socket Type
+ IPPROTO_TCP, // Protocol
+ kCFSocketConnectCallBack, // Callback flags
+ (CFSocketCallBack)&MyCFSocketCallback, // Callback method
+ &theContext); // Socket Context
+
+ if(theSocket4 == NULL)
+ {
+ if (errPtr) *errPtr = [self getSocketError];
+ return NO;
+ }
+ }
+ else if(pSockAddr->sa_family == AF_INET6)
+ {
+ theSocket6 = CFSocketCreate(NULL, // Default allocator
+ PF_INET6, // Protocol Family
+ SOCK_STREAM, // Socket Type
+ IPPROTO_TCP, // Protocol
+ kCFSocketConnectCallBack, // Callback flags
+ (CFSocketCallBack)&MyCFSocketCallback, // Callback method
+ &theContext); // Socket Context
+
+ if(theSocket6 == NULL)
+ {
+ if (errPtr) *errPtr = [self getSocketError];
+ return NO;
+ }
+ }
+ else
+ {
+ if (errPtr) *errPtr = [self getSocketError];
+ return NO;
+ }
+
+ return YES;
+}
+
+/**
+ * Adds the CFSocket's to the run-loop so that callbacks will work properly.
+**/
+- (BOOL)attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr
+{
+ // Get the CFRunLoop to which the socket should be attached.
+ theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop];
+
+ if(theSocket4)
+ {
+ theSource4 = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket4, 0);
+ [self runLoopAddSource:theSource4];
+ }
+
+ if(theSocket6)
+ {
+ theSource6 = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket6, 0);
+ [self runLoopAddSource:theSource6];
+ }
+
+ return YES;
+}
+
+/**
+ * Allows the delegate method to configure the CFSocket or CFNativeSocket as desired before we connect.
+ * Note that the CFReadStream and CFWriteStream will not be available until after the connection is opened.
+**/
+- (BOOL)configureSocketAndReturnError:(NSError **)errPtr
+{
+ // Call the delegate method for further configuration.
+ if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)])
+ {
+ if([theDelegate onSocketWillConnect:self] == NO)
+ {
+ if (errPtr) *errPtr = [self getAbortError];
+ return NO;
+ }
+ }
+ return YES;
+}
+
+- (BOOL)connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr
+{
+ // Start connecting to the given address in the background
+ // The MyCFSocketCallback method will be called when the connection succeeds or fails
+ if(theSocket4)
+ {
+ CFSocketError err = CFSocketConnectToAddress(theSocket4, (CFDataRef)remoteAddr, -1);
+ if(err != kCFSocketSuccess)
+ {
+ if (errPtr) *errPtr = [self getSocketError];
+ return NO;
+ }
+ }
+ else if(theSocket6)
+ {
+ CFSocketError err = CFSocketConnectToAddress(theSocket6, (CFDataRef)remoteAddr, -1);
+ if(err != kCFSocketSuccess)
+ {
+ if (errPtr) *errPtr = [self getSocketError];
+ return NO;
+ }
+ }
+
+ return YES;
+}
+
+/**
+ * Attempt to make the new socket.
+ * If an error occurs, ignore this event.
+**/
+- (void)doAcceptWithSocket:(CFSocketNativeHandle)newNative
+{
+ // New socket inherits same delegate and run loop modes.
+ // Note: We use [self class] to support subclassing AsyncSocket.
+ AsyncSocket *newSocket = [[[[self class] alloc] initWithDelegate:theDelegate] autorelease];
+ [newSocket setRunLoopModes:theRunLoopModes];
+
+ if(newSocket)
+ {
+ if ([theDelegate respondsToSelector:@selector(onSocket:didAcceptNewSocket:)])
+ [theDelegate onSocket:self didAcceptNewSocket:newSocket];
+
+ NSRunLoop *runLoop = nil;
+ if ([theDelegate respondsToSelector:@selector(onSocket:wantsRunLoopForNewSocket:)])
+ runLoop = [theDelegate onSocket:self wantsRunLoopForNewSocket:newSocket];
+
+ BOOL pass = YES;
+
+ if(pass && ![newSocket createStreamsFromNative:newNative error:nil]) pass = NO;
+ if(pass && ![newSocket attachStreamsToRunLoop:runLoop error:nil]) pass = NO;
+ if(pass && ![newSocket configureStreamsAndReturnError:nil]) pass = NO;
+ if(pass && ![newSocket openStreamsAndReturnError:nil]) pass = NO;
+
+ if(pass)
+ newSocket->theFlags |= kDidPassConnectMethod;
+ else {
+ // No NSError, but errors will still get logged from the above functions.
+ [newSocket close];
+ }
+
+ }
+}
+
+/**
+ * Description forthcoming...
+**/
+- (void)doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)socketError
+{
+ NSParameterAssert ((sock == theSocket4) || (sock == theSocket6));
+
+ if(socketError == kCFSocketTimeout || socketError == kCFSocketError)
+ {
+ [self closeWithError:[self getSocketError]];
+ return;
+ }
+
+ // Get the underlying native (BSD) socket
+ CFSocketNativeHandle nativeSocket = CFSocketGetNative(sock);
+
+ // Setup the socket so that invalidating the socket will not close the native socket
+ CFSocketSetSocketFlags(sock, 0);
+
+ // Invalidate and release the CFSocket - All we need from here on out is the nativeSocket
+ // Note: If we don't invalidate the socket (leaving the native socket open)
+ // then theReadStream and theWriteStream won't function properly.
+ // Specifically, their callbacks won't work, with the exception of kCFStreamEventOpenCompleted.
+ // I'm not entirely sure why this is, but I'm guessing that events on the socket fire to the CFSocket we created,
+ // as opposed to the CFReadStream/CFWriteStream.
+
+ CFSocketInvalidate(sock);
+ CFRelease(sock);
+ theSocket4 = NULL;
+ theSocket6 = NULL;
+
+ NSError *err;
+ BOOL pass = YES;
+
+ if(pass && ![self createStreamsFromNative:nativeSocket error:&err]) pass = NO;
+ if(pass && ![self attachStreamsToRunLoop:nil error:&err]) pass = NO;
+ if(pass && ![self openStreamsAndReturnError:&err]) pass = NO;
+
+ if(!pass)
+ {
+ [self closeWithError:err];
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Stream Implementation
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Creates the CFReadStream and CFWriteStream from the given native socket.
+ * The CFSocket may be extracted from either stream after the streams have been opened.
+ *
+ * Note: The given native socket must already be connected!
+**/
+- (BOOL)createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr
+{
+ // Create the socket & streams.
+ CFStreamCreatePairWithSocket(kCFAllocatorDefault, native, &theReadStream, &theWriteStream);
+ if (theReadStream == NULL || theWriteStream == NULL)
+ {
+ NSError *err = [self getStreamError];
+
+ NSLog (@"AsyncSocket %p couldn't create streams from accepted socket: %@", self, err);
+
+ if (errPtr) *errPtr = err;
+ return NO;
+ }
+
+ // Ensure the CF & BSD socket is closed when the streams are closed.
+ CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
+ CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
+
+ return YES;
+}
+
+/**
+ * Creates the CFReadStream and CFWriteStream from the given hostname and port number.
+ * The CFSocket may be extracted from either stream after the streams have been opened.
+**/
+- (BOOL)createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr
+{
+ // Create the socket & streams.
+ CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)hostname, port, &theReadStream, &theWriteStream);
+ if (theReadStream == NULL || theWriteStream == NULL)
+ {
+ if (errPtr) *errPtr = [self getStreamError];
+ return NO;
+ }
+
+ // Ensure the CF & BSD socket is closed when the streams are closed.
+ CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
+ CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
+
+ return YES;
+}
+
+- (BOOL)attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr
+{
+ // Get the CFRunLoop to which the socket should be attached.
+ theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop];
+
+ // Setup read stream callbacks
+
+ CFOptionFlags readStreamEvents = kCFStreamEventHasBytesAvailable |
+ kCFStreamEventErrorOccurred |
+ kCFStreamEventEndEncountered |
+ kCFStreamEventOpenCompleted;
+
+ if (!CFReadStreamSetClient(theReadStream,
+ readStreamEvents,
+ (CFReadStreamClientCallBack)&MyCFReadStreamCallback,
+ (CFStreamClientContext *)(&theContext)))
+ {
+ NSError *err = [self getStreamError];
+
+ NSLog (@"AsyncSocket %p couldn't attach read stream to run-loop,", self);
+ NSLog (@"Error: %@", err);
+
+ if (errPtr) *errPtr = err;
+ return NO;
+ }
+
+ // Setup write stream callbacks
+
+ CFOptionFlags writeStreamEvents = kCFStreamEventCanAcceptBytes |
+ kCFStreamEventErrorOccurred |
+ kCFStreamEventEndEncountered |
+ kCFStreamEventOpenCompleted;
+
+ if (!CFWriteStreamSetClient (theWriteStream,
+ writeStreamEvents,
+ (CFWriteStreamClientCallBack)&MyCFWriteStreamCallback,
+ (CFStreamClientContext *)(&theContext)))
+ {
+ NSError *err = [self getStreamError];
+
+ NSLog (@"AsyncSocket %p couldn't attach write stream to run-loop,", self);
+ NSLog (@"Error: %@", err);
+
+ if (errPtr) *errPtr = err;
+ return NO;
+ }
+
+ // Add read and write streams to run loop
+
+ unsigned i, count = [theRunLoopModes count];
+ for(i = 0; i < count; i++)
+ {
+ CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
+ CFReadStreamScheduleWithRunLoop(theReadStream, theRunLoop, runLoopMode);
+ CFWriteStreamScheduleWithRunLoop(theWriteStream, theRunLoop, runLoopMode);
+ }
+
+ return YES;
+}
+
+/**
+ * Allows the delegate method to configure the CFReadStream and/or CFWriteStream as desired before we connect.
+ * Note that the CFSocket and CFNativeSocket will not be available until after the connection is opened.
+**/
+- (BOOL)configureStreamsAndReturnError:(NSError **)errPtr
+{
+ // Call the delegate method for further configuration.
+ if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)])
+ {
+ if([theDelegate onSocketWillConnect:self] == NO)
+ {
+ if (errPtr) *errPtr = [self getAbortError];
+ return NO;
+ }
+ }
+ return YES;
+}
+
+- (BOOL)openStreamsAndReturnError:(NSError **)errPtr
+{
+ BOOL pass = YES;
+
+ if(pass && !CFReadStreamOpen (theReadStream))
+ {
+ NSLog (@"AsyncSocket %p couldn't open read stream,", self);
+ pass = NO;
+ }
+
+ if(pass && !CFWriteStreamOpen (theWriteStream))
+ {
+ NSLog (@"AsyncSocket %p couldn't open write stream,", self);
+ pass = NO;
+ }
+
+ if(!pass)
+ {
+ if (errPtr) *errPtr = [self getStreamError];
+ }
+
+ return pass;
+}
+
+/**
+ * Called when read or write streams open.
+ * When the socket is connected and both streams are open, consider the AsyncSocket instance to be ready.
+**/
+- (void)doStreamOpen
+{
+ NSError *err = nil;
+ if ((theFlags & kDidCompleteOpenForRead) && (theFlags & kDidCompleteOpenForWrite))
+ {
+ // Get the socket.
+ if (![self setSocketFromStreamsAndReturnError: &err])
+ {
+ NSLog (@"AsyncSocket %p couldn't get socket from streams, %@. Disconnecting.", self, err);
+ [self closeWithError:err];
+ return;
+ }
+
+ if ([theDelegate respondsToSelector:@selector(onSocket:didConnectToHost:port:)])
+ {
+ [theDelegate onSocket:self didConnectToHost:[self connectedHost] port:[self connectedPort]];
+ }
+
+ // Stop the connection attempt timeout timer
+ [self endConnectTimeout];
+
+ // Immediately deal with any already-queued requests.
+ [self maybeDequeueRead];
+ [self maybeDequeueWrite];
+ }
+}
+
+- (BOOL)setSocketFromStreamsAndReturnError:(NSError **)errPtr
+{
+ // Get the CFSocketNativeHandle from theReadStream
+ CFSocketNativeHandle native;
+ CFDataRef nativeProp = CFReadStreamCopyProperty(theReadStream, kCFStreamPropertySocketNativeHandle);
+ if(nativeProp == NULL)
+ {
+ if (errPtr) *errPtr = [self getStreamError];
+ return NO;
+ }
+
+ CFDataGetBytes(nativeProp, CFRangeMake(0, CFDataGetLength(nativeProp)), (UInt8 *)&native);
+ CFRelease(nativeProp);
+
+ CFSocketRef theSocket = CFSocketCreateWithNative(kCFAllocatorDefault, native, 0, NULL, NULL);
+ if(theSocket == NULL)
+ {
+ if (errPtr) *errPtr = [self getSocketError];
+ return NO;
+ }
+
+ // Determine whether the connection was IPv4 or IPv6
+ CFDataRef peeraddr = CFSocketCopyPeerAddress(theSocket);
+ if(peeraddr == NULL)
+ {
+ NSLog(@"AsyncSocket couldn't determine IP version of socket");
+
+ CFRelease(theSocket);
+
+ if (errPtr) *errPtr = [self getSocketError];
+ return NO;
+ }
+ struct sockaddr *sa = (struct sockaddr *)CFDataGetBytePtr(peeraddr);
+
+ if(sa->sa_family == AF_INET)
+ {
+ theSocket4 = theSocket;
+ }
+ else
+ {
+ theSocket6 = theSocket;
+ }
+
+ CFRelease(peeraddr);
+
+ return YES;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Disconnect Implementation
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Sends error message and disconnects
+- (void)closeWithError:(NSError *)err
+{
+ theFlags |= kClosingWithError;
+
+ if (theFlags & kDidPassConnectMethod)
+ {
+ // Try to salvage what data we can.
+ [self recoverUnreadData];
+
+ // Let the delegate know, so it can try to recover if it likes.
+ if ([theDelegate respondsToSelector:@selector(onSocket:willDisconnectWithError:)])
+ {
+ [theDelegate onSocket:self willDisconnectWithError:err];
+ }
+ }
+ [self close];
+}
+
+// Prepare partially read data for recovery.
+- (void)recoverUnreadData
+{
+ if((theCurrentRead != nil) && (theCurrentRead->bytesDone > 0))
+ {
+ // We never finished the current read.
+ // We need to move its data into the front of the partial read buffer.
+
+ [partialReadBuffer replaceBytesInRange:NSMakeRange(0, 0)
+ withBytes:[theCurrentRead->buffer bytes]
+ length:theCurrentRead->bytesDone];
+ }
+
+ [self emptyQueues];
+}
+
+- (void)emptyQueues
+{
+ if (theCurrentRead != nil) [self endCurrentRead];
+ if (theCurrentWrite != nil) [self endCurrentWrite];
+
+ [theReadQueue removeAllObjects];
+ [theWriteQueue removeAllObjects];
+
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueRead) object:nil];
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueWrite) object:nil];
+
+ theFlags &= ~kDequeueReadScheduled;
+ theFlags &= ~kDequeueWriteScheduled;
+}
+
+/**
+ * Disconnects. This is called for both error and clean disconnections.
+**/
+- (void)close
+{
+ // Empty queues
+ [self emptyQueues];
+
+ // Clear partialReadBuffer (pre-buffer and also unreadData buffer in case of error)
+ [partialReadBuffer replaceBytesInRange:NSMakeRange(0, [partialReadBuffer length]) withBytes:NULL length:0];
+
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(disconnect) object:nil];
+
+ // Stop the connection attempt timeout timer
+ if (theConnectTimer != nil)
+ {
+ [self endConnectTimeout];
+ }
+
+ // Close streams.
+ if (theReadStream != NULL)
+ {
+ [self runLoopUnscheduleReadStream];
+ CFReadStreamClose(theReadStream);
+ CFRelease(theReadStream);
+ theReadStream = NULL;
+ }
+ if (theWriteStream != NULL)
+ {
+ [self runLoopUnscheduleWriteStream];
+ CFWriteStreamClose(theWriteStream);
+ CFRelease(theWriteStream);
+ theWriteStream = NULL;
+ }
+
+ // Close sockets.
+ if (theSocket4 != NULL)
+ {
+ CFSocketInvalidate (theSocket4);
+ CFRelease (theSocket4);
+ theSocket4 = NULL;
+ }
+ if (theSocket6 != NULL)
+ {
+ CFSocketInvalidate (theSocket6);
+ CFRelease (theSocket6);
+ theSocket6 = NULL;
+ }
+ if (theSource4 != NULL)
+ {
+ [self runLoopRemoveSource:theSource4];
+ CFRelease (theSource4);
+ theSource4 = NULL;
+ }
+ if (theSource6 != NULL)
+ {
+ [self runLoopRemoveSource:theSource6];
+ CFRelease (theSource6);
+ theSource6 = NULL;
+ }
+ theRunLoop = NULL;
+
+ // If the client has passed the connect/accept method, then the connection has at least begun.
+ // Notify delegate that it is now ending.
+ if (theFlags & kDidPassConnectMethod)
+ {
+ // Delay notification to give dev freedom to release without returning here and core-dumping.
+ if ([theDelegate respondsToSelector: @selector(onSocketDidDisconnect:)])
+ {
+ [theDelegate performSelector:@selector(onSocketDidDisconnect:)
+ withObject:self
+ afterDelay:0
+ inModes:theRunLoopModes];
+ }
+ }
+
+ // Clear all flags (except the pre-buffering flag, which should remain as is)
+ theFlags &= kEnablePreBuffering;
+}
+
+/**
+ * Disconnects immediately. Any pending reads or writes are dropped.
+**/
+- (void)disconnect
+{
+ [self close];
+}
+
+/**
+ * Diconnects after all pending reads have completed.
+**/
+- (void)disconnectAfterReading
+{
+ theFlags |= (kForbidReadsWrites | kDisconnectAfterReads);
+
+ [self maybeScheduleDisconnect];
+}
+
+/**
+ * Disconnects after all pending writes have completed.
+**/
+- (void)disconnectAfterWriting
+{
+ theFlags |= (kForbidReadsWrites | kDisconnectAfterWrites);
+
+ [self maybeScheduleDisconnect];
+}
+
+/**
+ * Disconnects after all pending reads and writes have completed.
+**/
+- (void)disconnectAfterReadingAndWriting
+{
+ theFlags |= (kForbidReadsWrites | kDisconnectAfterReads | kDisconnectAfterWrites);
+
+ [self maybeScheduleDisconnect];
+}
+
+/**
+ * Schedules a call to disconnect if possible.
+ * That is, if all writes have completed, and we're set to disconnect after writing,
+ * or if all reads have completed, and we're set to disconnect after reading.
+**/
+- (void)maybeScheduleDisconnect
+{
+ BOOL shouldDisconnect = NO;
+
+ if(theFlags & kDisconnectAfterReads)
+ {
+ if(([theReadQueue count] == 0) && (theCurrentRead == nil))
+ {
+ if(theFlags & kDisconnectAfterWrites)
+ {
+ if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
+ {
+ shouldDisconnect = YES;
+ }
+ }
+ else
+ {
+ shouldDisconnect = YES;
+ }
+ }
+ }
+ else if(theFlags & kDisconnectAfterWrites)
+ {
+ if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
+ {
+ shouldDisconnect = YES;
+ }
+ }
+
+ if(shouldDisconnect)
+ {
+ [self performSelector:@selector(disconnect) withObject:nil afterDelay:0 inModes:theRunLoopModes];
+ }
+}
+
+/**
+ * In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read
+ * any data that's left on the socket.
+**/
+- (NSData *)unreadData
+{
+ // Ensure this method will only return data in the event of an error
+ if(!(theFlags & kClosingWithError)) return nil;
+
+ if(theReadStream == NULL) return nil;
+
+ CFIndex totalBytesRead = [partialReadBuffer length];
+ BOOL error = NO;
+ while(!error && CFReadStreamHasBytesAvailable(theReadStream))
+ {
+ [partialReadBuffer increaseLengthBy:READALL_CHUNKSIZE];
+
+ // Number of bytes to read is space left in packet buffer.
+ CFIndex bytesToRead = [partialReadBuffer length] - totalBytesRead;
+
+ // Read data into packet buffer
+ UInt8 *packetbuf = (UInt8 *)( [partialReadBuffer mutableBytes] + totalBytesRead );
+ CFIndex bytesRead = CFReadStreamRead(theReadStream, packetbuf, bytesToRead);
+
+ // Check results
+ if(bytesRead < 0)
+ {
+ error = YES;
+ }
+ else
+ {
+ totalBytesRead += bytesRead;
+ }
+ }
+
+ [partialReadBuffer setLength:totalBytesRead];
+
+ return partialReadBuffer;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Errors
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Returns a standard error object for the current errno value.
+ * Errno is used for low-level BSD socket errors.
+**/
+- (NSError *)getErrnoError
+{
+ NSString *errorMsg = [NSString stringWithUTF8String:strerror(errno)];
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errorMsg forKey:NSLocalizedDescriptionKey];
+
+ return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo];
+}
+
+/**
+ * Returns a standard error message for a CFSocket error.
+ * Unfortunately, CFSocket offers no feedback on its errors.
+**/
+- (NSError *)getSocketError
+{
+ NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCFSocketError",
+ @"AsyncSocket", [NSBundle mainBundle],
+ @"General CFSocket error", nil);
+
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCFSocketError userInfo:info];
+}
+
+- (NSError *) getStreamError
+{
+ CFStreamError err;
+ if (theReadStream != NULL)
+ {
+ err = CFReadStreamGetError (theReadStream);
+ if (err.error != 0) return [self errorFromCFStreamError: err];
+ }
+
+ if (theWriteStream != NULL)
+ {
+ err = CFWriteStreamGetError (theWriteStream);
+ if (err.error != 0) return [self errorFromCFStreamError: err];
+ }
+
+ return nil;
+}
+
+/**
+ * Returns a standard AsyncSocket abort error.
+**/
+- (NSError *)getAbortError
+{
+ NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCanceledError",
+ @"AsyncSocket", [NSBundle mainBundle],
+ @"Connection canceled", nil);
+
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCanceledError userInfo:info];
+}
+
+/**
+ * Returns a standard AsyncSocket connect timeout error.
+**/
+- (NSError *)getConnectTimeoutError
+{
+ NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketConnectTimeoutError",
+ @"AsyncSocket", [NSBundle mainBundle],
+ @"Attempt to connect to host timed out", nil);
+
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketConnectTimeoutError userInfo:info];
+}
+
+/**
+ * Returns a standard AsyncSocket maxed out error.
+**/
+- (NSError *)getReadMaxedOutError
+{
+ NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadMaxedOutError",
+ @"AsyncSocket", [NSBundle mainBundle],
+ @"Read operation reached set maximum length", nil);
+
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadMaxedOutError userInfo:info];
+}
+
+/**
+ * Returns a standard AsyncSocket read timeout error.
+**/
+- (NSError *)getReadTimeoutError
+{
+ NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadTimeoutError",
+ @"AsyncSocket", [NSBundle mainBundle],
+ @"Read operation timed out", nil);
+
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadTimeoutError userInfo:info];
+}
+
+/**
+ * Returns a standard AsyncSocket write timeout error.
+**/
+- (NSError *)getWriteTimeoutError
+{
+ NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketWriteTimeoutError",
+ @"AsyncSocket", [NSBundle mainBundle],
+ @"Write operation timed out", nil);
+
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketWriteTimeoutError userInfo:info];
+}
+
+- (NSError *)errorFromCFStreamError:(CFStreamError)err
+{
+ if (err.domain == 0 && err.error == 0) return nil;
+
+ // Can't use switch; these constants aren't int literals.
+ NSString *domain = @"CFStreamError (unlisted domain)";
+ NSString *message = nil;
+
+ if(err.domain == kCFStreamErrorDomainPOSIX) {
+ domain = NSPOSIXErrorDomain;
+ }
+ else if(err.domain == kCFStreamErrorDomainMacOSStatus) {
+ domain = NSOSStatusErrorDomain;
+ }
+ else if(err.domain == kCFStreamErrorDomainMach) {
+ domain = NSMachErrorDomain;
+ }
+ else if(err.domain == kCFStreamErrorDomainNetDB)
+ {
+ domain = @"kCFStreamErrorDomainNetDB";
+ message = [NSString stringWithCString:gai_strerror(err.error) encoding:NSASCIIStringEncoding];
+ }
+ else if(err.domain == kCFStreamErrorDomainNetServices) {
+ domain = @"kCFStreamErrorDomainNetServices";
+ }
+ else if(err.domain == kCFStreamErrorDomainSOCKS) {
+ domain = @"kCFStreamErrorDomainSOCKS";
+ }
+ else if(err.domain == kCFStreamErrorDomainSystemConfiguration) {
+ domain = @"kCFStreamErrorDomainSystemConfiguration";
+ }
+ else if(err.domain == kCFStreamErrorDomainSSL) {
+ domain = @"kCFStreamErrorDomainSSL";
+ }
+
+ NSDictionary *info = nil;
+ if(message != nil)
+ {
+ info = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey];
+ }
+ return [NSError errorWithDomain:domain code:err.error userInfo:info];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Diagnostics
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (BOOL)isConnected
+{
+ return [self isSocketConnected] && [self areStreamsConnected];
+}
+
+- (NSString *)connectedHost
+{
+ if(theSocket4)
+ return [self connectedHost:theSocket4];
+ else
+ return [self connectedHost:theSocket6];
+}
+
+- (UInt16)connectedPort
+{
+ if(theSocket4)
+ return [self connectedPort:theSocket4];
+ else
+ return [self connectedPort:theSocket6];
+}
+
+- (NSString *)localHost
+{
+ if(theSocket4)
+ return [self localHost:theSocket4];
+ else
+ return [self localHost:theSocket6];
+}
+
+- (UInt16)localPort
+{
+ if(theSocket4)
+ return [self localPort:theSocket4];
+ else
+ return [self localPort:theSocket6];
+}
+
+- (NSString *)connectedHost:(CFSocketRef)theSocket
+{
+ if (theSocket == NULL) return nil;
+
+ CFDataRef peeraddr;
+ NSString *peerstr = nil;
+
+ if((peeraddr = CFSocketCopyPeerAddress(theSocket)))
+ {
+ peerstr = [self addressHost:peeraddr];
+ CFRelease (peeraddr);
+ }
+
+ return peerstr;
+}
+
+- (UInt16)connectedPort:(CFSocketRef)theSocket
+{
+ if (theSocket == NULL) return 0;
+
+ CFDataRef peeraddr;
+ UInt16 peerport = 0;
+
+ if((peeraddr = CFSocketCopyPeerAddress(theSocket)))
+ {
+ peerport = [self addressPort:peeraddr];
+ CFRelease (peeraddr);
+ }
+
+ return peerport;
+}
+
+- (NSString *)localHost:(CFSocketRef)theSocket
+{
+ if (theSocket == NULL) return nil;
+
+ CFDataRef selfaddr;
+ NSString *selfstr = nil;
+
+ if((selfaddr = CFSocketCopyAddress(theSocket)))
+ {
+ selfstr = [self addressHost:selfaddr];
+ CFRelease (selfaddr);
+ }
+
+ return selfstr;
+}
+
+- (UInt16)localPort:(CFSocketRef)theSocket
+{
+ if (theSocket == NULL) return 0;
+
+ CFDataRef selfaddr;
+ UInt16 selfport = 0;
+
+ if ((selfaddr = CFSocketCopyAddress(theSocket)))
+ {
+ selfport = [self addressPort:selfaddr];
+ CFRelease (selfaddr);
+ }
+
+ return selfport;
+}
+
+- (BOOL)isSocketConnected
+{
+ if(theSocket4 != NULL)
+ return CFSocketIsValid(theSocket4);
+ else if(theSocket6 != NULL)
+ return CFSocketIsValid(theSocket6);
+ else
+ return NO;
+}
+
+- (BOOL)areStreamsConnected
+{
+ CFStreamStatus s;
+
+ if (theReadStream != NULL)
+ {
+ s = CFReadStreamGetStatus (theReadStream);
+ if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusReading || s == kCFStreamStatusError) )
+ return NO;
+ }
+ else return NO;
+
+ if (theWriteStream != NULL)
+ {
+ s = CFWriteStreamGetStatus (theWriteStream);
+ if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusWriting || s == kCFStreamStatusError) )
+ return NO;
+ }
+ else return NO;
+
+ return YES;
+}
+
+- (NSString *)addressHost:(CFDataRef)cfaddr
+{
+ if (cfaddr == NULL) return nil;
+
+ char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
+ struct sockaddr *pSockAddr = (struct sockaddr *) CFDataGetBytePtr (cfaddr);
+ struct sockaddr_in *pSockAddrV4 = (struct sockaddr_in *) pSockAddr;
+ struct sockaddr_in6 *pSockAddrV6 = (struct sockaddr_in6 *)pSockAddr;
+
+ const void *pAddr = (pSockAddr->sa_family == AF_INET) ?
+ (void *)(&(pSockAddrV4->sin_addr)) :
+ (void *)(&(pSockAddrV6->sin6_addr));
+
+ const char *pStr = inet_ntop (pSockAddr->sa_family, pAddr, addrBuf, sizeof(addrBuf));
+ if (pStr == NULL) [NSException raise: NSInternalInconsistencyException
+ format: @"Cannot convert address to string."];
+
+ return [NSString stringWithCString:pStr encoding:NSASCIIStringEncoding];
+}
+
+- (UInt16)addressPort:(CFDataRef)cfaddr
+{
+ if (cfaddr == NULL) return 0;
+ struct sockaddr_in *pAddr = (struct sockaddr_in *) CFDataGetBytePtr (cfaddr);
+ return ntohs (pAddr->sin_port);
+}
+
+- (BOOL)isIPv4
+{
+ return (theSocket4 != NULL);
+}
+
+- (BOOL)isIPv6
+{
+ return (theSocket6 != NULL);
+}
+
+- (NSString *)description
+{
+ static const char *statstr[] = {"not open","opening","open","reading","writing","at end","closed","has error"};
+ CFStreamStatus rs = (theReadStream != NULL) ? CFReadStreamGetStatus(theReadStream) : 0;
+ CFStreamStatus ws = (theWriteStream != NULL) ? CFWriteStreamGetStatus(theWriteStream) : 0;
+
+ NSString *peerstr, *selfstr;
+ CFDataRef peeraddr4 = NULL, peeraddr6 = NULL, selfaddr4 = NULL, selfaddr6 = NULL;
+
+ if (theSocket4 || theSocket6)
+ {
+ if (theSocket4) peeraddr4 = CFSocketCopyPeerAddress(theSocket4);
+ if (theSocket6) peeraddr6 = CFSocketCopyPeerAddress(theSocket6);
+
+ if(theSocket4 && theSocket6)
+ {
+ peerstr = [NSString stringWithFormat: @"%@/%@ %u",
+ [self addressHost:peeraddr4], [self addressHost:peeraddr6], [self addressPort:peeraddr4]];
+ }
+ else if(theSocket4)
+ {
+ peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr4], [self addressPort:peeraddr4]];
+ }
+ else
+ {
+ peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr6], [self addressPort:peeraddr6]];
+ }
+
+ if(peeraddr4) CFRelease(peeraddr4);
+ if(peeraddr6) CFRelease(peeraddr6);
+ peeraddr4 = NULL;
+ peeraddr6 = NULL;
+ }
+ else peerstr = @"nowhere";
+
+ if (theSocket4 || theSocket6)
+ {
+ if (theSocket4) selfaddr4 = CFSocketCopyAddress (theSocket4);
+ if (theSocket6) selfaddr6 = CFSocketCopyAddress (theSocket6);
+
+ if (theSocket4 && theSocket6)
+ {
+ selfstr = [NSString stringWithFormat: @"%@/%@ %u",
+ [self addressHost:selfaddr4], [self addressHost:selfaddr6], [self addressPort:selfaddr4]];
+ }
+ else if (theSocket4)
+ {
+ selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr4], [self addressPort:selfaddr4]];
+ }
+ else
+ {
+ selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr6], [self addressPort:selfaddr6]];
+ }
+
+ if(selfaddr4) CFRelease(selfaddr4);
+ if(selfaddr6) CFRelease(selfaddr6);
+ selfaddr4 = NULL;
+ selfaddr6 = NULL;
+ }
+ else selfstr = @"nowhere";
+
+ NSMutableString *ms = [[NSMutableString alloc] initWithCapacity:150];
+
+ [ms appendString:[NSString stringWithFormat:@"<AsyncSocket %p", self]];
+ [ms appendString:[NSString stringWithFormat:@" local %@ remote %@ ", selfstr, peerstr]];
+
+ unsigned readQueueCount = (unsigned)[theReadQueue count];
+ unsigned writeQueueCount = (unsigned)[theWriteQueue count];
+
+ [ms appendString:[NSString stringWithFormat:@"has queued %u reads %u writes, ", readQueueCount, writeQueueCount]];
+
+ if (theCurrentRead == nil)
+ [ms appendString: @"no current read, "];
+ else
+ {
+ int percentDone;
+ if ([theCurrentRead->buffer length] != 0)
+ percentDone = (float)theCurrentRead->bytesDone /
+ (float)[theCurrentRead->buffer length] * 100.0F;
+ else
+ percentDone = 100.0F;
+
+ [ms appendString: [NSString stringWithFormat:@"currently read %u bytes (%d%% done), ",
+ (unsigned int)[theCurrentRead->buffer length],
+ theCurrentRead->bytesDone ? percentDone : 0]];
+ }
+
+ if (theCurrentWrite == nil)
+ [ms appendString: @"no current write, "];
+ else
+ {
+ int percentDone;
+ if ([theCurrentWrite->buffer length] != 0)
+ percentDone = (float)theCurrentWrite->bytesDone /
+ (float)[theCurrentWrite->buffer length] * 100.0F;
+ else
+ percentDone = 100.0F;
+
+ [ms appendString: [NSString stringWithFormat:@"currently written %u (%d%%), ",
+ (unsigned int)[theCurrentWrite->buffer length],
+ theCurrentWrite->bytesDone ? percentDone : 0]];
+ }
+
+ [ms appendString:[NSString stringWithFormat:@"read stream %p %s, ", theReadStream, statstr[rs]]];
+ [ms appendString:[NSString stringWithFormat:@"write stream %p %s", theWriteStream, statstr[ws]]];
+
+ if(theFlags & kDisconnectAfterReads)
+ {
+ if(theFlags & kDisconnectAfterWrites)
+ [ms appendString: @", will disconnect after reads & writes"];
+ else
+ [ms appendString: @", will disconnect after reads"];
+ }
+ else if(theFlags & kDisconnectAfterWrites)
+ {
+ [ms appendString: @", will disconnect after writes"];
+ }
+
+ if (![self isConnected]) [ms appendString: @", not connected"];
+
+ [ms appendString:@">"];
+
+ return [ms autorelease];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Reading
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
+{
+ if(length == 0) return;
+ if(theFlags & kForbidReadsWrites) return;
+
+ NSMutableData *buffer = [[NSMutableData alloc] initWithLength:length];
+ AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
+ timeout:timeout
+ tag:tag
+ readAllAvailable:NO
+ terminator:nil
+ maxLength:length];
+
+ [theReadQueue addObject:packet];
+ [self scheduleDequeueRead];
+
+ [packet release];
+ [buffer release];
+}
+
+- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
+{
+ [self readDataToData:data withTimeout:timeout maxLength:-1 tag:tag];
+}
+
+- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag
+{
+ if(data == nil || [data length] == 0) return;
+ if(length >= 0 && length < [data length]) return;
+ if(theFlags & kForbidReadsWrites) return;
+
+ NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0];
+ AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
+ timeout:timeout
+ tag:tag
+ readAllAvailable:NO
+ terminator:data
+ maxLength:length];
+
+ [theReadQueue addObject:packet];
+ [self scheduleDequeueRead];
+
+ [packet release];
+ [buffer release];
+}
+
+- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag
+{
+ if (theFlags & kForbidReadsWrites) return;
+
+ NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0];
+ AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
+ timeout:timeout
+ tag:tag
+ readAllAvailable:YES
+ terminator:nil
+ maxLength:-1];
+
+ [theReadQueue addObject:packet];
+ [self scheduleDequeueRead];
+
+ [packet release];
+ [buffer release];
+}
+
+/**
+ * Puts a maybeDequeueRead on the run loop.
+ * An assumption here is that selectors will be performed consecutively within their priority.
+**/
+- (void)scheduleDequeueRead
+{
+ if((theFlags & kDequeueReadScheduled) == 0)
+ {
+ theFlags |= kDequeueReadScheduled;
+ [self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
+ }
+}
+
+/**
+ * This method starts a new read, if needed.
+ * It is called when a user requests a read,
+ * or when a stream opens that may have requested reads sitting in the queue, etc.
+**/
+- (void)maybeDequeueRead
+{
+ // Unset the flag indicating a call to this method is scheduled
+ theFlags &= ~kDequeueReadScheduled;
+
+ // If we're not currently processing a read AND we have an available read stream
+ if((theCurrentRead == nil) && (theReadStream != NULL))
+ {
+ if([theReadQueue count] > 0)
+ {
+ // Dequeue the next object in the write queue
+ theCurrentRead = [[theReadQueue objectAtIndex:0] retain];
+ [theReadQueue removeObjectAtIndex:0];
+
+ if([theCurrentRead isKindOfClass:[AsyncSpecialPacket class]])
+ {
+ // Attempt to start TLS
+ // This method won't do anything unless both theCurrentRead and theCurrentWrite are start TLS packets
+ [self maybeStartTLS];
+ }
+ else
+ {
+ // Start time-out timer
+ if(theCurrentRead->timeout >= 0.0)
+ {
+ theReadTimer = [NSTimer timerWithTimeInterval:theCurrentRead->timeout
+ target:self
+ selector:@selector(doReadTimeout:)
+ userInfo:nil
+ repeats:NO];
+ [self runLoopAddTimer:theReadTimer];
+ }
+
+ // Immediately read, if possible
+ [self doBytesAvailable];
+ }
+ }
+ else if(theFlags & kDisconnectAfterReads)
+ {
+ if(theFlags & kDisconnectAfterWrites)
+ {
+ if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
+ {
+ [self disconnect];
+ }
+ }
+ else
+ {
+ [self disconnect];
+ }
+ }
+ }
+}
+
+/**
+ * Call this method in doBytesAvailable instead of CFReadStreamHasBytesAvailable().
+ * This method supports pre-buffering properly.
+**/
+- (BOOL)hasBytesAvailable
+{
+ return ([partialReadBuffer length] > 0) || CFReadStreamHasBytesAvailable(theReadStream);
+}
+
+/**
+ * Call this method in doBytesAvailable instead of CFReadStreamRead().
+ * This method support pre-buffering properly.
+**/
+- (CFIndex)readIntoBuffer:(UInt8 *)buffer maxLength:(CFIndex)length
+{
+ if([partialReadBuffer length] > 0)
+ {
+ // Determine the maximum amount of data to read
+ CFIndex bytesToRead = MIN(length, [partialReadBuffer length]);
+
+ // Copy the bytes from the buffer
+ memcpy(buffer, [partialReadBuffer bytes], bytesToRead);
+
+ // Remove the copied bytes from the buffer
+ [partialReadBuffer replaceBytesInRange:NSMakeRange(0, bytesToRead) withBytes:NULL length:0];
+
+ return bytesToRead;
+ }
+ else
+ {
+ return CFReadStreamRead(theReadStream, buffer, length);
+ }
+}
+
+/**
+ * This method is called when a new read is taken from the read queue or when new data becomes available on the stream.
+**/
+- (void)doBytesAvailable
+{
+ // If data is available on the stream, but there is no read request, then we don't need to process the data yet.
+ // Also, if there is a read request, but no read stream setup yet, we can't process any data yet.
+ if(theCurrentRead != nil && theReadStream != NULL)
+ {
+ CFIndex totalBytesRead = 0;
+
+ BOOL done = NO;
+ BOOL socketError = NO;
+ BOOL maxoutError = NO;
+
+ while(!done && !socketError && !maxoutError && [self hasBytesAvailable])
+ {
+ BOOL didPreBuffer = NO;
+
+ // If reading all available data, make sure there's room in the packet buffer.
+ if(theCurrentRead->readAllAvailableData == YES)
+ {
+ // Make sure there is at least READALL_CHUNKSIZE bytes available.
+ // We don't want to increase the buffer any more than this or we'll waste space.
+ // With prebuffering it's possible to read in a small chunk on the first read.
+
+ unsigned buffInc = READALL_CHUNKSIZE - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
+ [theCurrentRead->buffer increaseLengthBy:buffInc];
+ }
+
+ // If reading until data, we may only want to read a few bytes.
+ // Just enough to ensure we don't go past our term or over our max limit.
+ // Unless pre-buffering is enabled, in which case we may want to read in a larger chunk.
+ if(theCurrentRead->term != nil)
+ {
+ // If we already have data pre-buffered, we obviously don't want to pre-buffer it again.
+ // So in this case we'll just read as usual.
+
+ if(([partialReadBuffer length] > 0) || !(theFlags & kEnablePreBuffering))
+ {
+ unsigned maxToRead = [theCurrentRead readLengthForTerm];
+
+ unsigned bufInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
+ [theCurrentRead->buffer increaseLengthBy:bufInc];
+ }
+ else
+ {
+ didPreBuffer = YES;
+ unsigned maxToRead = [theCurrentRead prebufferReadLengthForTerm];
+
+ unsigned buffInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
+ [theCurrentRead->buffer increaseLengthBy:buffInc];
+
+ }
+ }
+
+ // Number of bytes to read is space left in packet buffer.
+ CFIndex bytesToRead = [theCurrentRead->buffer length] - theCurrentRead->bytesDone;
+
+ // Read data into packet buffer
+ UInt8 *subBuffer = (UInt8 *)([theCurrentRead->buffer mutableBytes] + theCurrentRead->bytesDone);
+ CFIndex bytesRead = [self readIntoBuffer:subBuffer maxLength:bytesToRead];
+
+ // Check results
+ if(bytesRead < 0)
+ {
+ socketError = YES;
+ }
+ else
+ {
+ // Update total amound read for the current read
+ theCurrentRead->bytesDone += bytesRead;
+
+ // Update total amount read in this method invocation
+ totalBytesRead += bytesRead;
+ }
+
+ // Is packet done?
+ if(theCurrentRead->readAllAvailableData != YES)
+ {
+ if(theCurrentRead->term != nil)
+ {
+ if(didPreBuffer)
+ {
+ // Search for the terminating sequence within the big chunk we just read.
+ CFIndex overflow = [theCurrentRead searchForTermAfterPreBuffering:bytesRead];
+
+ if(overflow > 0)
+ {
+ // Copy excess data into partialReadBuffer
+ NSMutableData *buffer = theCurrentRead->buffer;
+ const void *overflowBuffer = [buffer bytes] + theCurrentRead->bytesDone - overflow;
+
+ [partialReadBuffer appendBytes:overflowBuffer length:overflow];
+
+ // Update the bytesDone variable.
+ // Note: The completeCurrentRead method will trim the buffer for us.
+ theCurrentRead->bytesDone -= overflow;
+ }
+
+ done = (overflow >= 0);
+ }
+ else
+ {
+ // Search for the terminating sequence at the end of the buffer
+ int termlen = [theCurrentRead->term length];
+ if(theCurrentRead->bytesDone >= termlen)
+ {
+ const void *buf = [theCurrentRead->buffer bytes] + (theCurrentRead->bytesDone - termlen);
+ const void *seq = [theCurrentRead->term bytes];
+ done = (memcmp (buf, seq, termlen) == 0);
+ }
+ }
+
+ if(!done && theCurrentRead->maxLength >= 0 && theCurrentRead->bytesDone >= theCurrentRead->maxLength)
+ {
+ // There's a set maxLength, and we've reached that maxLength without completing the read
+ maxoutError = YES;
+ }
+ }
+ else
+ {
+ // Done when (sized) buffer is full.
+ done = ([theCurrentRead->buffer length] == theCurrentRead->bytesDone);
+ }
+ }
+ // else readAllAvailable doesn't end until all readable is read.
+ }
+
+ if(theCurrentRead->readAllAvailableData && theCurrentRead->bytesDone > 0)
+ done = YES; // Ran out of bytes, so the "read-all-data" type packet is done
+
+ if(done)
+ {
+ [self completeCurrentRead];
+ if (!socketError) [self scheduleDequeueRead];
+ }
+ else if(theCurrentRead->bytesDone > 0)
+ {
+ // We're not done with the readToLength or readToData yet, but we have read in some bytes
+ if ([theDelegate respondsToSelector:@selector(onSocket:didReadPartialDataOfLength:tag:)])
+ {
+ [theDelegate onSocket:self didReadPartialDataOfLength:totalBytesRead tag:theCurrentRead->tag];
+ }
+ }
+
+ if(socketError)
+ {
+ CFStreamError err = CFReadStreamGetError(theReadStream);
+ [self closeWithError:[self errorFromCFStreamError:err]];
+ return;
+ }
+ if(maxoutError)
+ {
+ [self closeWithError:[self getReadMaxedOutError]];
+ return;
+ }
+ }
+}
+
+// Ends current read and calls delegate.
+- (void)completeCurrentRead
+{
+ NSAssert (theCurrentRead, @"Trying to complete current read when there is no current read.");
+
+ [theCurrentRead->buffer setLength:theCurrentRead->bytesDone];
+ if([theDelegate respondsToSelector:@selector(onSocket:didReadData:withTag:)])
+ {
+ [theDelegate onSocket:self didReadData:theCurrentRead->buffer withTag:theCurrentRead->tag];
+ }
+
+ if (theCurrentRead != nil) [self endCurrentRead]; // Caller may have disconnected.
+}
+
+// Ends current read.
+- (void)endCurrentRead
+{
+ NSAssert (theCurrentRead, @"Trying to end current read when there is no current read.");
+
+ [theReadTimer invalidate];
+ theReadTimer = nil;
+
+ [theCurrentRead release];
+ theCurrentRead = nil;
+}
+
+- (void)doReadTimeout:(NSTimer *)timer
+{
+ if (theCurrentRead != nil)
+ {
+ [self endCurrentRead];
+ }
+ [self closeWithError:[self getReadTimeoutError]];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Writing
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
+{
+ if (data == nil || [data length] == 0) return;
+ if (theFlags & kForbidReadsWrites) return;
+
+ AsyncWritePacket *packet = [[AsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag];
+
+ [theWriteQueue addObject:packet];
+ [self scheduleDequeueWrite];
+
+ [packet release];
+}
+
+- (void)scheduleDequeueWrite
+{
+ if((theFlags & kDequeueWriteScheduled) == 0)
+ {
+ theFlags |= kDequeueWriteScheduled;
+ [self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0 inModes:theRunLoopModes];
+ }
+}
+
+// Start a new write.
+- (void)maybeDequeueWrite
+{
+ // Unset the flag indicating a call to this method is scheduled
+ theFlags &= ~kDequeueWriteScheduled;
+
+ // If we're not currently processing a write AND we have an available write stream
+ if((theCurrentWrite == nil) && (theWriteStream != NULL))
+ {
+ if([theWriteQueue count] > 0)
+ {
+ // Dequeue the next object in the write queue
+ theCurrentWrite = [[theWriteQueue objectAtIndex:0] retain];
+ [theWriteQueue removeObjectAtIndex:0];
+
+ if([theCurrentWrite isKindOfClass:[AsyncSpecialPacket class]])
+ {
+ // Attempt to start TLS
+ // This method won't do anything unless both theCurrentWrite and theCurrentRead are start TLS packets
+ [self maybeStartTLS];
+ }
+ else
+ {
+ // Start time-out timer
+ if(theCurrentWrite->timeout >= 0.0)
+ {
+ theWriteTimer = [NSTimer timerWithTimeInterval:theCurrentWrite->timeout
+ target:self
+ selector:@selector(doWriteTimeout:)
+ userInfo:nil
+ repeats:NO];
+ [self runLoopAddTimer:theWriteTimer];
+ }
+
+ // Immediately write, if possible
+ [self doSendBytes];
+ }
+ }
+ else if(theFlags & kDisconnectAfterWrites)
+ {
+ if(theFlags & kDisconnectAfterReads)
+ {
+ if(([theReadQueue count] == 0) && (theCurrentRead == nil))
+ {
+ [self disconnect];
+ }
+ }
+ else
+ {
+ [self disconnect];
+ }
+ }
+ }
+}
+
+- (void)doSendBytes
+{
+ if((theCurrentWrite != nil) && (theWriteStream != NULL))
+ {
+ BOOL done = NO, error = NO;
+ while (!done && !error && CFWriteStreamCanAcceptBytes(theWriteStream))
+ {
+ // Figure out what to write.
+ CFIndex bytesRemaining = [theCurrentWrite->buffer length] - theCurrentWrite->bytesDone;
+ CFIndex bytesToWrite = (bytesRemaining < WRITE_CHUNKSIZE) ? bytesRemaining : WRITE_CHUNKSIZE;
+ UInt8 *writestart = (UInt8 *)([theCurrentWrite->buffer bytes] + theCurrentWrite->bytesDone);
+
+ // Write.
+ CFIndex bytesWritten = CFWriteStreamWrite (theWriteStream, writestart, bytesToWrite);
+
+ // Check results.
+ if (bytesWritten < 0)
+ {
+ bytesWritten = 0;
+ error = YES;
+ }
+
+ // Is packet done?
+ theCurrentWrite->bytesDone += bytesWritten;
+ done = ([theCurrentWrite->buffer length] == theCurrentWrite->bytesDone);
+ }
+
+ if(done)
+ {
+ [self completeCurrentWrite];
+ if (!error) [self scheduleDequeueWrite];
+ }
+
+ if(error)
+ {
+ CFStreamError err = CFWriteStreamGetError(theWriteStream);
+ [self closeWithError:[self errorFromCFStreamError:err]];
+ return;
+ }
+ }
+}
+
+// Ends current write and calls delegate.
+- (void)completeCurrentWrite
+{
+ NSAssert (theCurrentWrite, @"Trying to complete current write when there is no current write.");
+
+ if ([theDelegate respondsToSelector:@selector(onSocket:didWriteDataWithTag:)])
+ {
+ [theDelegate onSocket:self didWriteDataWithTag:theCurrentWrite->tag];
+ }
+
+ if (theCurrentWrite != nil) [self endCurrentWrite]; // Caller may have disconnected.
+}
+
+// Ends current write.
+- (void)endCurrentWrite
+{
+ NSAssert (theCurrentWrite, @"Trying to complete current write when there is no current write.");
+
+ [theWriteTimer invalidate];
+ theWriteTimer = nil;
+
+ [theCurrentWrite release];
+ theCurrentWrite = nil;
+}
+
+- (void)doWriteTimeout:(NSTimer *)timer
+{
+ if(theCurrentWrite != nil)
+ {
+ [self endCurrentWrite];
+ }
+ [self closeWithError:[self getWriteTimeoutError]];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Security
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)startTLS:(NSDictionary *)tlsSettings
+{
+ if([tlsSettings count] == 0) return;
+
+ AsyncSpecialPacket *packet = [[AsyncSpecialPacket alloc] initWithTLSSettings:tlsSettings];
+
+ [theReadQueue addObject:packet];
+ [self scheduleDequeueRead];
+
+ [theWriteQueue addObject:packet];
+ [self scheduleDequeueWrite];
+
+ [packet release];
+}
+
+- (void)maybeStartTLS
+{
+ NSAssert((theFlags & kStartingTLS) == 0, @"Trying to start TLS after TLS has already been started");
+
+ // We can't start TLS until:
+ // - All queued reads prior to the user calling StartTLS are complete
+ // - All queued writes prior to the user calling StartTLS are complete
+ //
+ // We'll know these conditions are met when both theCurrentRead and theCurrentWrite variables
+ // are of type AsyncSpecialPacket.
+
+ Class SpecialPacketClass = [AsyncSpecialPacket class];
+
+ if([theCurrentRead isKindOfClass:SpecialPacketClass] && [theCurrentWrite isKindOfClass:SpecialPacketClass])
+ {
+ theFlags |= kStartingTLS;
+
+ AsyncSpecialPacket *tlsPacket = (AsyncSpecialPacket *)theCurrentRead;
+
+ BOOL didSecureReadStream = CFReadStreamSetProperty(theReadStream, kCFStreamPropertySSLSettings,
+ (CFDictionaryRef)tlsPacket->tlsSettings);
+ BOOL didSecureWriteStream = CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertySSLSettings,
+ (CFDictionaryRef)tlsPacket->tlsSettings);
+
+ if(!didSecureReadStream || !didSecureWriteStream)
+ {
+ [self onTLSStarted:NO];
+ }
+ }
+}
+
+- (void)onTLSStarted:(BOOL)flag
+{
+ theFlags &= ~kStartingTLS;
+
+ if([theDelegate respondsToSelector:@selector(onSocket:didSecure:)])
+ {
+ [theDelegate onSocket:self didSecure:flag];
+ }
+
+ [self endCurrentRead];
+ [self endCurrentWrite];
+
+ [self scheduleDequeueRead];
+ [self scheduleDequeueWrite];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark CF Callbacks
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)doCFSocketCallback:(CFSocketCallBackType)type
+ forSocket:(CFSocketRef)sock
+ withAddress:(NSData *)address
+ withData:(const void *)pData
+{
+ NSParameterAssert ((sock == theSocket4) || (sock == theSocket6));
+
+ switch (type)
+ {
+ case kCFSocketConnectCallBack:
+ // The data argument is either NULL or a pointer to an SInt32 error code, if the connect failed.
+ if(pData)
+ [self doSocketOpen:sock withCFSocketError:kCFSocketError];
+ else
+ [self doSocketOpen:sock withCFSocketError:kCFSocketSuccess];
+ break;
+ case kCFSocketAcceptCallBack:
+ [self doAcceptWithSocket: *((CFSocketNativeHandle *)pData)];
+ break;
+ default:
+ NSLog (@"AsyncSocket %p received unexpected CFSocketCallBackType %d.", self, type);
+ break;
+ }
+}
+
+- (void)doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream
+{
+ NSParameterAssert(theReadStream != NULL);
+
+ CFStreamError err;
+ switch (type)
+ {
+ case kCFStreamEventOpenCompleted:
+ theFlags |= kDidCompleteOpenForRead;
+ [self doStreamOpen];
+ break;
+ case kCFStreamEventHasBytesAvailable:
+ if(theFlags & kStartingTLS)
+ [self onTLSStarted:YES];
+ else
+ [self doBytesAvailable];
+ break;
+ case kCFStreamEventErrorOccurred:
+ case kCFStreamEventEndEncountered:
+ err = CFReadStreamGetError (theReadStream);
+ [self closeWithError: [self errorFromCFStreamError:err]];
+ break;
+ default:
+ NSLog (@"AsyncSocket %p received unexpected CFReadStream callback, CFStreamEventType %d.", self, type);
+ }
+}
+
+- (void)doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream
+{
+ NSParameterAssert(theWriteStream != NULL);
+
+ CFStreamError err;
+ switch (type)
+ {
+ case kCFStreamEventOpenCompleted:
+ theFlags |= kDidCompleteOpenForWrite;
+ [self doStreamOpen];
+ break;
+ case kCFStreamEventCanAcceptBytes:
+ if(theFlags & kStartingTLS)
+ [self onTLSStarted:YES];
+ else
+ [self doSendBytes];
+ break;
+ case kCFStreamEventErrorOccurred:
+ case kCFStreamEventEndEncountered:
+ err = CFWriteStreamGetError (theWriteStream);
+ [self closeWithError: [self errorFromCFStreamError:err]];
+ break;
+ default:
+ NSLog (@"AsyncSocket %p received unexpected CFWriteStream callback, CFStreamEventType %d.", self, type);
+ }
+}
+
+/**
+ * This is the callback we setup for CFSocket.
+ * This method does nothing but forward the call to it's Objective-C counterpart
+**/
+static void MyCFSocketCallback (CFSocketRef sref, CFSocketCallBackType type, CFDataRef address, const void *pData, void *pInfo)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease];
+ [theSocket doCFSocketCallback:type forSocket:sref withAddress:(NSData *)address withData:pData];
+
+ [pool release];
+}
+
+/**
+ * This is the callback we setup for CFReadStream.
+ * This method does nothing but forward the call to it's Objective-C counterpart
+**/
+static void MyCFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease];
+ [theSocket doCFReadStreamCallback:type forStream:stream];
+
+ [pool release];
+}
+
+/**
+ * This is the callback we setup for CFWriteStream.
+ * This method does nothing but forward the call to it's Objective-C counterpart
+**/
+static void MyCFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease];
+ [theSocket doCFWriteStreamCallback:type forStream:stream];
+
+ [pool release];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Class Methods
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Return line separators.
++ (NSData *)CRLFData
+{
+ return [NSData dataWithBytes:"\x0D\x0A" length:2];
+}
+
++ (NSData *)CRData
+{
+ return [NSData dataWithBytes:"\x0D" length:1];
+}
+
++ (NSData *)LFData
+{
+ return [NSData dataWithBytes:"\x0A" length:1];
+}
+
++ (NSData *)ZeroData
+{
+ return [NSData dataWithBytes:"" length:1];
+}
+
+@end
--- /dev/null
+//
+// AsyncUdpSocket.h
+//
+// This class is in the public domain.
+// Originally created by Robbie Hanson on Wed Oct 01 2008.
+// Updated and maintained by Deusty Designs and the Mac development community.
+//
+// http://code.google.com/p/cocoaasyncsocket/
+//
+
+#import <Foundation/Foundation.h>
+
+@class AsyncSendPacket;
+@class AsyncReceivePacket;
+
+extern NSString *const AsyncUdpSocketException;
+extern NSString *const AsyncUdpSocketErrorDomain;
+
+enum AsyncUdpSocketError
+{
+ AsyncUdpSocketCFSocketError = kCFSocketError, // From CFSocketError enum
+ AsyncUdpSocketNoError = 0, // Never used
+ AsyncUdpSocketBadParameter, // Used if given a bad parameter (such as an improper address)
+ AsyncUdpSocketIPv4Unavailable, // Used if you bind/connect using IPv6 only
+ AsyncUdpSocketIPv6Unavailable, // Used if you bind/connect using IPv4 only (or iPhone)
+ AsyncUdpSocketSendTimeoutError,
+ AsyncUdpSocketReceiveTimeoutError
+};
+typedef enum AsyncUdpSocketError AsyncUdpSocketError;
+
+@interface AsyncUdpSocket : NSObject
+{
+ CFSocketRef theSocket4; // IPv4 socket
+ CFSocketRef theSocket6; // IPv6 socket
+
+ CFRunLoopSourceRef theSource4; // For theSocket4
+ CFRunLoopSourceRef theSource6; // For theSocket6
+ CFRunLoopRef theRunLoop;
+ CFSocketContext theContext;
+ NSArray *theRunLoopModes;
+
+ NSMutableArray *theSendQueue;
+ AsyncSendPacket *theCurrentSend;
+ NSTimer *theSendTimer;
+
+ NSMutableArray *theReceiveQueue;
+ AsyncReceivePacket *theCurrentReceive;
+ NSTimer *theReceiveTimer;
+
+ id theDelegate;
+ UInt16 theFlags;
+
+ long theUserData;
+
+ NSString *cachedLocalHost;
+ UInt16 cachedLocalPort;
+
+ NSString *cachedConnectedHost;
+ UInt16 cachedConnectedPort;
+
+ UInt32 maxReceiveBufferSize;
+}
+
+/**
+ * Creates new instances of AsyncUdpSocket.
+**/
+- (id)init;
+- (id)initWithDelegate:(id)delegate;
+- (id)initWithDelegate:(id)delegate userData:(long)userData;
+
+/**
+ * Creates new instances of AsyncUdpSocket that support only IPv4 or IPv6.
+ * The other init methods will support both, unless specifically binded or connected to one protocol.
+ * If you know you'll only be using one protocol, these init methods may be a bit more efficient.
+**/
+- (id)initIPv4;
+- (id)initIPv6;
+
+- (id)delegate;
+- (void)setDelegate:(id)delegate;
+
+- (long)userData;
+- (void)setUserData:(long)userData;
+
+/**
+ * Returns the local address info for the socket.
+ *
+ * Note: Address info may not be available until after the socket has been bind'ed,
+ * or until after data has been sent.
+**/
+- (NSString *)localHost;
+- (UInt16)localPort;
+
+/**
+ * Returns the remote address info for the socket.
+ *
+ * Note: Since UDP is connectionless by design, connected address info
+ * will not be available unless the socket is explicitly connected to a remote host/port
+**/
+- (NSString *)connectedHost;
+- (UInt16)connectedPort;
+
+/**
+ * Returns whether or not this socket has been connected to a single host.
+ * By design, UDP is a connectionless protocol, and connecting is not needed.
+ * If connected, the socket will only be able to send/receive data to/from the connected host.
+**/
+- (BOOL)isConnected;
+
+/**
+ * Returns whether or not this socket has been closed.
+ * The only way a socket can be closed is if you explicitly call one of the close methods.
+**/
+- (BOOL)isClosed;
+
+/**
+ * Returns whether or not this socket supports IPv4.
+ * By default this will be true, unless the socket is specifically initialized as IPv6 only,
+ * or is binded or connected to an IPv6 address.
+**/
+- (BOOL)isIPv4;
+
+/**
+ * Returns whether or not this socket supports IPv6.
+ * By default this will be true, unless the socket is specifically initialized as IPv4 only,
+ * or is binded or connected to an IPv4 address.
+ *
+ * This method will also return false on platforms that do not support IPv6.
+ * Note: The iPhone does not currently support IPv6.
+**/
+- (BOOL)isIPv6;
+
+/**
+ * Returns the mtu of the socket.
+ * If unknown, returns zero.
+ *
+ * Sending data larger than this may result in an error.
+ * This is an advanced topic, and one should understand the wide range of mtu's on networks and the internet.
+ * Therefore this method is only for reference and may be of little use in many situations.
+**/
+- (unsigned int)maximumTransmissionUnit;
+
+/**
+ * Binds the UDP socket to the given port and optional address.
+ * Binding should be done for server sockets that receive data prior to sending it.
+ * Client sockets can skip binding,
+ * as the OS will automatically assign the socket an available port when it starts sending data.
+ *
+ * You cannot bind a socket after its been connected.
+ * You can only bind a socket once.
+ * You can still connect a socket (if desired) after binding.
+ *
+ * On success, returns YES.
+ * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr.
+**/
+- (BOOL)bindToPort:(UInt16)port error:(NSError **)errPtr;
+- (BOOL)bindToAddress:(NSString *)localAddr port:(UInt16)port error:(NSError **)errPtr;
+
+/**
+ * Connects the UDP socket to the given host and port.
+ * By design, UDP is a connectionless protocol, and connecting is not needed.
+ *
+ * Choosing to connect to a specific host/port has the following effect:
+ * - You will only be able to send data to the connected host/port.
+ * - You will only be able to receive data from the connected host/port.
+ * - You will receive ICMP messages that come from the connected host/port, such as "connection refused".
+ *
+ * Connecting a UDP socket does not result in any communication on the socket.
+ * It simply changes the internal state of the socket.
+ *
+ * You cannot bind a socket after its been connected.
+ * You can only connect a socket once.
+ *
+ * On success, returns YES.
+ * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr.
+**/
+- (BOOL)connectToHost:(NSString *)host onPort:(UInt16)port error:(NSError **)errPtr;
+- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
+
+/**
+ * Join multicast group
+ *
+ * Group should be an IP address (eg @"225.228.0.1")
+**/
+- (BOOL)joinMulticastGroup:(NSString *)group error:(NSError **)errPtr;
+- (BOOL)joinMulticastGroup:(NSString *)group withAddress:(NSString *)interface error:(NSError **)errPtr;
+
+/**
+ * By default, the underlying socket in the OS will not allow you to send broadcast messages.
+ * In order to send broadcast messages, you need to enable this functionality in the socket.
+ *
+ * A broadcast is a UDP message to addresses like "192.168.255.255" or "255.255.255.255" that is
+ * delivered to every host on the network.
+ * The reason this is generally disabled by default is to prevent
+ * accidental broadcast messages from flooding the network.
+**/
+- (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr;
+
+/**
+ * Asynchronously sends the given data, with the given timeout and tag.
+ *
+ * This method may only be used with a connected socket.
+ *
+ * If data is nil or zero-length, this method does nothing and immediately returns NO.
+ * If the socket is not connected, this method does nothing and immediately returns NO.
+**/
+- (BOOL)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
+
+/**
+ * Asynchronously sends the given data, with the given timeout and tag, to the given host and port.
+ *
+ * This method cannot be used with a connected socket.
+ *
+ * If data is nil or zero-length, this method does nothing and immediately returns NO.
+ * If the socket is connected, this method does nothing and immediately returns NO.
+ * If unable to resolve host to a valid IPv4 or IPv6 address, this method returns NO.
+**/
+- (BOOL)sendData:(NSData *)data toHost:(NSString *)host port:(UInt16)port withTimeout:(NSTimeInterval)timeout tag:(long)tag;
+
+/**
+ * Asynchronously sends the given data, with the given timeout and tag, to the given address.
+ *
+ * This method cannot be used with a connected socket.
+ *
+ * If data is nil or zero-length, this method does nothing and immediately returns NO.
+ * If the socket is connected, this method does nothing and immediately returns NO.
+**/
+- (BOOL)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag;
+
+/**
+ * Asynchronously receives a single datagram packet.
+ *
+ * If the receive succeeds, the onUdpSocket:didReceiveData:fromHost:port:tag delegate method will be called.
+ * Otherwise, a timeout will occur, and the onUdpSocket:didNotReceiveDataWithTag: delegate method will be called.
+**/
+- (void)receiveWithTimeout:(NSTimeInterval)timeout tag:(long)tag;
+
+/**
+ * Closes the socket immediately. Any pending send or receive operations are dropped.
+**/
+- (void)close;
+
+/**
+ * Closes after all pending send operations have completed.
+ * After calling this, the sendData: and receive: methods will do nothing.
+ * In other words, you won't be able to add any more send or receive operations to the queue.
+ * The socket will close even if there are still pending receive operations.
+**/
+- (void)closeAfterSending;
+
+/**
+ * Closes after all pending receive operations have completed.
+ * After calling this, the sendData: and receive: methods will do nothing.
+ * In other words, you won't be able to add any more send or receive operations to the queue.
+ * The socket will close even if there are still pending send operations.
+**/
+- (void)closeAfterReceiving;
+
+/**
+ * Closes after all pending send and receive operations have completed.
+ * After calling this, the sendData: and receive: methods will do nothing.
+ * In other words, you won't be able to add any more send or receive operations to the queue.
+**/
+- (void)closeAfterSendingAndReceiving;
+
+/**
+ * Gets/Sets the maximum size of the buffer that will be allocated for receive operations.
+ * The default size is 9216 bytes.
+ *
+ * The theoretical maximum size of any IPv4 UDP packet is UINT16_MAX = 65535.
+ * The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295.
+ *
+ * In practice, however, the size of UDP packets will be much smaller.
+ * Indeed most protocols will send and receive packets of only a few bytes,
+ * or will set a limit on the size of packets to prevent fragmentation in the IP layer.
+ *
+ * If you set the buffer size too small, the sockets API in the OS will silently discard
+ * any extra data, and you will not be notified of the error.
+**/
+- (UInt32)maxReceiveBufferSize;
+- (void)setMaxReceiveBufferSize:(UInt32)max;
+
+/**
+ * When you create an AsyncUdpSocket, it is added to the runloop of the current thread.
+ * So it is easiest to simply create the socket on the thread you intend to use it.
+ *
+ * If, however, you need to move the socket to a separate thread at a later time, this
+ * method may be used to accomplish the task.
+ *
+ * This method must be called from the thread/runloop the socket is currently running on.
+ *
+ * Note: After calling this method, all further method calls to this object should be done from the given runloop.
+ * Also, all delegate calls will be sent on the given runloop.
+**/
+- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop;
+
+/**
+ * Allows you to configure which run loop modes the socket uses.
+ * The default set of run loop modes is NSDefaultRunLoopMode.
+ *
+ * If you'd like your socket to continue operation during other modes, you may want to add modes such as
+ * NSModalPanelRunLoopMode or NSEventTrackingRunLoopMode. Or you may simply want to use NSRunLoopCommonModes.
+ *
+ * Note: NSRunLoopCommonModes is defined in 10.5. For previous versions one can use kCFRunLoopCommonModes.
+**/
+- (BOOL)setRunLoopModes:(NSArray *)runLoopModes;
+
+/**
+ * Returns the current run loop modes the AsyncSocket instance is operating in.
+ * The default set of run loop modes is NSDefaultRunLoopMode.
+**/
+- (NSArray *)runLoopModes;
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@interface NSObject (AsyncUdpSocketDelegate)
+
+/**
+ * Called when the datagram with the given tag has been sent.
+**/
+- (void)onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag;
+
+/**
+ * Called if an error occurs while trying to send a datagram.
+ * This could be due to a timeout, or something more serious such as the data being too large to fit in a sigle packet.
+**/
+- (void)onUdpSocket:(AsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error;
+
+/**
+ * Called when the socket has received the requested datagram.
+ *
+ * Due to the nature of UDP, you may occasionally receive undesired packets.
+ * These may be rogue UDP packets from unknown hosts,
+ * or they may be delayed packets arriving after retransmissions have already occurred.
+ * It's important these packets are properly ignored, while not interfering with the flow of your implementation.
+ * As an aid, this delegate method has a boolean return value.
+ * If you ever need to ignore a received packet, simply return NO,
+ * and AsyncUdpSocket will continue as if the packet never arrived.
+ * That is, the original receive request will still be queued, and will still timeout as usual if a timeout was set.
+ * For example, say you requested to receive data, and you set a timeout of 500 milliseconds, using a tag of 15.
+ * If rogue data arrives after 250 milliseconds, this delegate method would be invoked, and you could simply return NO.
+ * If the expected data then arrives within the next 250 milliseconds,
+ * this delegate method will be invoked, with a tag of 15, just as if the rogue data never appeared.
+ *
+ * Under normal circumstances, you simply return YES from this method.
+**/
+- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port;
+
+/**
+ * Called if an error occurs while trying to receive a requested datagram.
+ * This is generally due to a timeout, but could potentially be something else if some kind of OS error occurred.
+**/
+- (void)onUdpSocket:(AsyncUdpSocket *)sock didNotReceiveDataWithTag:(long)tag dueToError:(NSError *)error;
+
+/**
+ * Called when the socket is closed.
+ * A socket is only closed if you explicitly call one of the close methods.
+**/
+- (void)onUdpSocketDidClose:(AsyncUdpSocket *)sock;
+
+@end
--- /dev/null
+//
+// AsyncUdpSocket.m
+//
+// This class is in the public domain.
+// Originally created by Robbie Hanson on Wed Oct 01 2008.
+// Updated and maintained by Deusty Designs and the Mac development community.
+//
+// http://code.google.com/p/cocoaasyncsocket/
+//
+
+#import "AsyncUdpSocket.h"
+#import <sys/socket.h>
+#import <netinet/in.h>
+#import <arpa/inet.h>
+#import <sys/ioctl.h>
+#import <net/if.h>
+#import <netdb.h>
+
+#if TARGET_OS_IPHONE
+// Note: You may need to add the CFNetwork Framework to your project
+#import <CFNetwork/CFNetwork.h>
+#endif
+
+
+#define SENDQUEUE_CAPACITY 5 // Initial capacity
+#define RECEIVEQUEUE_CAPACITY 5 // Initial capacity
+
+#define DEFAULT_MAX_RECEIVE_BUFFER_SIZE 9216
+
+NSString *const AsyncUdpSocketException = @"AsyncUdpSocketException";
+NSString *const AsyncUdpSocketErrorDomain = @"AsyncUdpSocketErrorDomain";
+
+// This is a mutex lock used by all instances of AsyncSocket, to protect getaddrinfo.
+// The man page says it is not thread-safe. (As of Mac OS X 10.4.7, and possibly earlier)
+static NSString *getaddrinfoLock = @"lock";
+
+enum AsyncUdpSocketFlags
+{
+ kDidBind = 1 << 0, // If set, bind has been called.
+ kDidConnect = 1 << 1, // If set, connect has been called.
+ kSock4CanAcceptBytes = 1 << 2, // If set, we know socket4 can accept bytes. If unset, it's unknown.
+ kSock6CanAcceptBytes = 1 << 3, // If set, we know socket6 can accept bytes. If unset, it's unknown.
+ kSock4HasBytesAvailable = 1 << 4, // If set, we know socket4 has bytes available. If unset, it's unknown.
+ kSock6HasBytesAvailable = 1 << 5, // If set, we know socket6 has bytes available. If unset, it's unknown.
+ kForbidSendReceive = 1 << 6, // If set, no new send or receive operations are allowed to be queued.
+ kCloseAfterSends = 1 << 7, // If set, close as soon as no more sends are queued.
+ kCloseAfterReceives = 1 << 8, // If set, close as soon as no more receives are queued.
+ kDidClose = 1 << 9, // If set, the socket has been closed, and should not be used anymore.
+ kDequeueSendScheduled = 1 << 10, // If set, a maybeDequeueSend operation is already scheduled.
+ kDequeueReceiveScheduled = 1 << 11, // If set, a maybeDequeueReceive operation is already scheduled.
+ kFlipFlop = 1 << 12, // Used to alternate between IPv4 and IPv6 sockets.
+};
+
+@interface AsyncUdpSocket (Private)
+
+// Run Loop
+- (void)runLoopAddSource:(CFRunLoopSourceRef)source;
+- (void)runLoopRemoveSource:(CFRunLoopSourceRef)source;
+- (void)runLoopAddTimer:(NSTimer *)timer;
+- (void)runLoopRemoveTimer:(NSTimer *)timer;
+
+// Utilities
+- (NSString *)addressHost4:(struct sockaddr_in *)pSockaddr4;
+- (NSString *)addressHost6:(struct sockaddr_in6 *)pSockaddr6;
+- (NSString *)addressHost:(struct sockaddr *)pSockaddr;
+
+// Disconnect Implementation
+- (void)emptyQueues;
+- (void)closeSocket4;
+- (void)closeSocket6;
+- (void)maybeScheduleClose;
+
+// Errors
+- (NSError *)getErrnoError;
+- (NSError *)getSocketError;
+- (NSError *)getIPv4UnavailableError;
+- (NSError *)getIPv6UnavailableError;
+- (NSError *)getSendTimeoutError;
+- (NSError *)getReceiveTimeoutError;
+
+// Diagnostics
+- (NSString *)connectedHost:(CFSocketRef)socket;
+- (UInt16)connectedPort:(CFSocketRef)socket;
+- (NSString *)localHost:(CFSocketRef)socket;
+- (UInt16)localPort:(CFSocketRef)socket;
+
+// Sending
+- (BOOL)canAcceptBytes:(CFSocketRef)sockRef;
+- (void)scheduleDequeueSend;
+- (void)maybeDequeueSend;
+- (void)doSend:(CFSocketRef)sockRef;
+- (void)completeCurrentSend;
+- (void)failCurrentSend:(NSError *)error;
+- (void)endCurrentSend;
+- (void)doSendTimeout:(NSTimer *)timer;
+
+// Receiving
+- (BOOL)hasBytesAvailable:(CFSocketRef)sockRef;
+- (void)scheduleDequeueReceive;
+- (void)maybeDequeueReceive;
+- (void)doReceive4;
+- (void)doReceive6;
+- (void)doReceive:(CFSocketRef)sockRef;
+- (BOOL)maybeCompleteCurrentReceive;
+- (void)failCurrentReceive:(NSError *)error;
+- (void)endCurrentReceive;
+- (void)doReceiveTimeout:(NSTimer *)timer;
+
+@end
+
+static void MyCFSocketCallback(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *);
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * The AsyncSendPacket encompasses the instructions for a single send/write.
+**/
+@interface AsyncSendPacket : NSObject
+{
+@public
+ NSData *buffer;
+ NSData *address;
+ NSTimeInterval timeout;
+ long tag;
+}
+- (id)initWithData:(NSData *)d address:(NSData *)a timeout:(NSTimeInterval)t tag:(long)i;
+@end
+
+@implementation AsyncSendPacket
+
+- (id)initWithData:(NSData *)d address:(NSData *)a timeout:(NSTimeInterval)t tag:(long)i
+{
+ if((self = [super init]))
+ {
+ buffer = [d retain];
+ address = [a retain];
+ timeout = t;
+ tag = i;
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [buffer release];
+ [address release];
+ [super dealloc];
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * The AsyncReceivePacket encompasses the instructions for a single receive/read.
+**/
+@interface AsyncReceivePacket : NSObject
+{
+@public
+ NSTimeInterval timeout;
+ long tag;
+ NSMutableData *buffer;
+ NSString *host;
+ UInt16 port;
+}
+- (id)initWithTimeout:(NSTimeInterval)t tag:(long)i;
+@end
+
+@implementation AsyncReceivePacket
+
+- (id)initWithTimeout:(NSTimeInterval)t tag:(long)i
+{
+ if((self = [super init]))
+ {
+ timeout = t;
+ tag = i;
+
+ buffer = nil;
+ host = nil;
+ port = 0;
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [buffer release];
+ [host release];
+ [super dealloc];
+}
+
+@end
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark -
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+@implementation AsyncUdpSocket
+
+- (id)initWithDelegate:(id)delegate userData:(long)userData enableIPv4:(BOOL)enableIPv4 enableIPv6:(BOOL)enableIPv6
+{
+ if((self = [super init]))
+ {
+ theFlags = 0;
+ theDelegate = delegate;
+ theUserData = userData;
+ maxReceiveBufferSize = DEFAULT_MAX_RECEIVE_BUFFER_SIZE;
+
+ theSendQueue = [[NSMutableArray alloc] initWithCapacity:SENDQUEUE_CAPACITY];
+ theCurrentSend = nil;
+ theSendTimer = nil;
+
+ theReceiveQueue = [[NSMutableArray alloc] initWithCapacity:RECEIVEQUEUE_CAPACITY];
+ theCurrentReceive = nil;
+ theReceiveTimer = nil;
+
+ // Socket context
+ theContext.version = 0;
+ theContext.info = self;
+ theContext.retain = nil;
+ theContext.release = nil;
+ theContext.copyDescription = nil;
+
+ // Create the sockets
+ theSocket4 = NULL;
+ theSocket6 = NULL;
+
+ if(enableIPv4)
+ {
+ theSocket4 = CFSocketCreate(kCFAllocatorDefault,
+ PF_INET,
+ SOCK_DGRAM,
+ IPPROTO_UDP,
+ kCFSocketReadCallBack | kCFSocketWriteCallBack,
+ (CFSocketCallBack)&MyCFSocketCallback,
+ &theContext);
+ }
+ if(enableIPv6)
+ {
+ theSocket6 = CFSocketCreate(kCFAllocatorDefault,
+ PF_INET6,
+ SOCK_DGRAM,
+ IPPROTO_UDP,
+ kCFSocketReadCallBack | kCFSocketWriteCallBack,
+ (CFSocketCallBack)&MyCFSocketCallback,
+ &theContext);
+ }
+
+ // Disable continuous callbacks for read and write.
+ // If we don't do this, the socket(s) will just sit there firing read callbacks
+ // at us hundreds of times a second if we don't immediately read the available data.
+ if(theSocket4)
+ {
+ CFSocketSetSocketFlags(theSocket4, kCFSocketCloseOnInvalidate);
+ }
+ if(theSocket6)
+ {
+ CFSocketSetSocketFlags(theSocket6, kCFSocketCloseOnInvalidate);
+ }
+
+ // Get the CFRunLoop to which the socket should be attached.
+ theRunLoop = CFRunLoopGetCurrent();
+
+ // Set default run loop modes
+ theRunLoopModes = [[NSArray arrayWithObject:NSDefaultRunLoopMode] retain];
+
+ // Attach the sockets to the run loop
+
+ if(theSocket4)
+ {
+ theSource4 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, theSocket4, 0);
+ [self runLoopAddSource:theSource4];
+ }
+
+ if(theSocket6)
+ {
+ theSource6 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, theSocket6, 0);
+ [self runLoopAddSource:theSource6];
+ }
+
+ cachedLocalPort = 0;
+ cachedConnectedPort = 0;
+ }
+ return self;
+}
+
+- (id)init
+{
+ return [self initWithDelegate:nil userData:0 enableIPv4:YES enableIPv6:YES];
+}
+
+- (id)initWithDelegate:(id)delegate
+{
+ return [self initWithDelegate:delegate userData:0 enableIPv4:YES enableIPv6:YES];
+}
+
+- (id)initWithDelegate:(id)delegate userData:(long)userData
+{
+ return [self initWithDelegate:delegate userData:userData enableIPv4:YES enableIPv6:YES];
+}
+
+- (id)initIPv4
+{
+ return [self initWithDelegate:nil userData:0 enableIPv4:YES enableIPv6:NO];
+}
+
+- (id)initIPv6
+{
+ return [self initWithDelegate:nil userData:0 enableIPv4:NO enableIPv6:YES];
+}
+
+- (void) dealloc
+{
+ [self close];
+ [theSendQueue release];
+ [theReceiveQueue release];
+ [theRunLoopModes release];
+ [cachedLocalHost release];
+ [cachedConnectedHost release];
+ [NSObject cancelPreviousPerformRequestsWithTarget:theDelegate selector:@selector(onUdpSocketDidClose:) object:self];
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+ [super dealloc];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Accessors
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (id)delegate
+{
+ return theDelegate;
+}
+
+- (void)setDelegate:(id)delegate
+{
+ theDelegate = delegate;
+}
+
+- (long)userData
+{
+ return theUserData;
+}
+
+- (void)setUserData:(long)userData
+{
+ theUserData = userData;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Run Loop
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)runLoopAddSource:(CFRunLoopSourceRef)source
+{
+ unsigned i, count = [theRunLoopModes count];
+ for(i = 0; i < count; i++)
+ {
+ CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
+ CFRunLoopAddSource(theRunLoop, source, runLoopMode);
+ }
+}
+
+- (void)runLoopRemoveSource:(CFRunLoopSourceRef)source
+{
+ unsigned i, count = [theRunLoopModes count];
+ for(i = 0; i < count; i++)
+ {
+ CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
+ CFRunLoopRemoveSource(theRunLoop, source, runLoopMode);
+ }
+}
+
+- (void)runLoopAddTimer:(NSTimer *)timer
+{
+ unsigned i, count = [theRunLoopModes count];
+ for(i = 0; i < count; i++)
+ {
+ CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
+ CFRunLoopAddTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode);
+ }
+}
+
+- (void)runLoopRemoveTimer:(NSTimer *)timer
+{
+ unsigned i, count = [theRunLoopModes count];
+ for(i = 0; i < count; i++)
+ {
+ CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
+ CFRunLoopRemoveTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Configuration
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (UInt32)maxReceiveBufferSize
+{
+ return maxReceiveBufferSize;
+}
+
+- (void)setMaxReceiveBufferSize:(UInt32)max
+{
+ maxReceiveBufferSize = max;
+}
+
+/**
+ * See the header file for a full explanation of this method.
+**/
+- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop
+{
+ NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()),
+ @"moveToRunLoop must be called from within the current RunLoop!");
+
+ if(runLoop == nil)
+ {
+ return NO;
+ }
+ if(theRunLoop == [runLoop getCFRunLoop])
+ {
+ return YES;
+ }
+
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+ theFlags &= ~kDequeueSendScheduled;
+ theFlags &= ~kDequeueReceiveScheduled;
+
+ if(theSource4) [self runLoopRemoveSource:theSource4];
+ if(theSource6) [self runLoopRemoveSource:theSource6];
+
+ // We do not retain the timers - they get retained by the runloop when we add them as a source.
+ // Since we're about to remove them as a source, we retain now, and release again below.
+ [theSendTimer retain];
+ [theReceiveTimer retain];
+
+ if(theSendTimer) [self runLoopRemoveTimer:theSendTimer];
+ if(theReceiveTimer) [self runLoopRemoveTimer:theReceiveTimer];
+
+ theRunLoop = [runLoop getCFRunLoop];
+
+ if(theSendTimer) [self runLoopAddTimer:theSendTimer];
+ if(theReceiveTimer) [self runLoopAddTimer:theReceiveTimer];
+
+ // Release timers since we retained them above
+ [theSendTimer release];
+ [theReceiveTimer release];
+
+ if(theSource4) [self runLoopAddSource:theSource4];
+ if(theSource6) [self runLoopAddSource:theSource6];
+
+ [runLoop performSelector:@selector(maybeDequeueSend) target:self argument:nil order:0 modes:theRunLoopModes];
+ [runLoop performSelector:@selector(maybeDequeueReceive) target:self argument:nil order:0 modes:theRunLoopModes];
+ [runLoop performSelector:@selector(maybeScheduleClose) target:self argument:nil order:0 modes:theRunLoopModes];
+
+ return YES;
+}
+
+/**
+ * See the header file for a full explanation of this method.
+**/
+- (BOOL)setRunLoopModes:(NSArray *)runLoopModes
+{
+ NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()),
+ @"setRunLoopModes must be called from within the current RunLoop!");
+
+ if([runLoopModes count] == 0)
+ {
+ return NO;
+ }
+ if([theRunLoopModes isEqualToArray:runLoopModes])
+ {
+ return YES;
+ }
+
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+ theFlags &= ~kDequeueSendScheduled;
+ theFlags &= ~kDequeueReceiveScheduled;
+
+ if(theSource4) [self runLoopRemoveSource:theSource4];
+ if(theSource6) [self runLoopRemoveSource:theSource6];
+
+ // We do not retain the timers - they get retained by the runloop when we add them as a source.
+ // Since we're about to remove them as a source, we retain now, and release again below.
+ [theSendTimer retain];
+ [theReceiveTimer retain];
+
+ if(theSendTimer) [self runLoopRemoveTimer:theSendTimer];
+ if(theReceiveTimer) [self runLoopRemoveTimer:theReceiveTimer];
+
+ [theRunLoopModes release];
+ theRunLoopModes = [runLoopModes copy];
+
+ if(theSendTimer) [self runLoopAddTimer:theSendTimer];
+ if(theReceiveTimer) [self runLoopAddTimer:theReceiveTimer];
+
+ // Release timers since we retained them above
+ [theSendTimer release];
+ [theReceiveTimer release];
+
+ if(theSource4) [self runLoopAddSource:theSource4];
+ if(theSource6) [self runLoopAddSource:theSource6];
+
+ [self performSelector:@selector(maybeDequeueSend) withObject:nil afterDelay:0 inModes:theRunLoopModes];
+ [self performSelector:@selector(maybeDequeueReceive) withObject:nil afterDelay:0 inModes:theRunLoopModes];
+ [self performSelector:@selector(maybeScheduleClose) withObject:nil afterDelay:0 inModes:theRunLoopModes];
+
+ return YES;
+}
+
+- (NSArray *)runLoopModes
+{
+ return [[theRunLoopModes retain] autorelease];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Utilities:
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Attempts to convert the given host/port into and IPv4 and/or IPv6 data structure.
+ * The data structure is of type sockaddr_in for IPv4 and sockaddr_in6 for IPv6.
+ *
+ * Returns zero on success, or one of the error codes listed in gai_strerror if an error occurs (as per getaddrinfo).
+**/
+- (int)convertForBindHost:(NSString *)host
+ port:(UInt16)port
+ intoAddress4:(NSData **)address4
+ address6:(NSData **)address6
+{
+ if(host == nil || ([host length] == 0))
+ {
+ // Use ANY address
+ struct sockaddr_in nativeAddr;
+ nativeAddr.sin_len = sizeof(struct sockaddr_in);
+ nativeAddr.sin_family = AF_INET;
+ nativeAddr.sin_port = htons(port);
+ nativeAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero));
+
+ struct sockaddr_in6 nativeAddr6;
+ nativeAddr6.sin6_len = sizeof(struct sockaddr_in6);
+ nativeAddr6.sin6_family = AF_INET6;
+ nativeAddr6.sin6_port = htons(port);
+ nativeAddr6.sin6_flowinfo = 0;
+ nativeAddr6.sin6_addr = in6addr_any;
+ nativeAddr6.sin6_scope_id = 0;
+
+ // Wrap the native address structures for CFSocketSetAddress.
+ if(address4) *address4 = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)];
+ if(address6) *address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
+
+ return 0;
+ }
+ else if([host isEqualToString:@"localhost"] || [host isEqualToString:@"loopback"])
+ {
+ // Note: getaddrinfo("localhost",...) fails on 10.5.3
+
+ // Use LOOPBACK address
+ struct sockaddr_in nativeAddr;
+ nativeAddr.sin_len = sizeof(struct sockaddr_in);
+ nativeAddr.sin_family = AF_INET;
+ nativeAddr.sin_port = htons(port);
+ nativeAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero));
+
+ struct sockaddr_in6 nativeAddr6;
+ nativeAddr6.sin6_len = sizeof(struct sockaddr_in6);
+ nativeAddr6.sin6_family = AF_INET6;
+ nativeAddr6.sin6_port = htons(port);
+ nativeAddr6.sin6_flowinfo = 0;
+ nativeAddr6.sin6_addr = in6addr_loopback;
+ nativeAddr6.sin6_scope_id = 0;
+
+ // Wrap the native address structures for CFSocketSetAddress.
+ if(address4) *address4 = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)];
+ if(address6) *address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
+
+ return 0;
+ }
+ else
+ {
+ NSString *portStr = [NSString stringWithFormat:@"%hu", port];
+
+ @synchronized (getaddrinfoLock)
+ {
+ struct addrinfo hints, *res, *res0;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ hints.ai_flags = AI_PASSIVE;
+
+ int error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0);
+
+ if(error) return error;
+
+ for(res = res0; res; res = res->ai_next)
+ {
+ if(address4 && !*address4 && (res->ai_family == AF_INET))
+ {
+ // Found IPv4 address
+ // Wrap the native address structures for CFSocketSetAddress.
+ if(address4) *address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
+ }
+ else if(address6 && !*address6 && (res->ai_family == AF_INET6))
+ {
+ // Found IPv6 address
+ // Wrap the native address structures for CFSocketSetAddress.
+ if(address6) *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
+ }
+ }
+ freeaddrinfo(res0);
+ }
+
+ return 0;
+ }
+}
+
+/**
+ * Attempts to convert the given host/port into and IPv4 and/or IPv6 data structure.
+ * The data structure is of type sockaddr_in for IPv4 and sockaddr_in6 for IPv6.
+ *
+ * Returns zero on success, or one of the error codes listed in gai_strerror if an error occurs (as per getaddrinfo).
+**/
+- (int)convertForSendHost:(NSString *)host
+ port:(UInt16)port
+ intoAddress4:(NSData **)address4
+ address6:(NSData **)address6
+{
+ if(host == nil || ([host length] == 0))
+ {
+ // We're not binding, so what are we supposed to do with this?
+ return EAI_NONAME;
+ }
+ else if([host isEqualToString:@"localhost"] || [host isEqualToString:@"loopback"])
+ {
+ // Note: getaddrinfo("localhost",...) fails on 10.5.3
+
+ // Use LOOPBACK address
+ struct sockaddr_in nativeAddr;
+ nativeAddr.sin_len = sizeof(struct sockaddr_in);
+ nativeAddr.sin_family = AF_INET;
+ nativeAddr.sin_port = htons(port);
+ nativeAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero));
+
+ struct sockaddr_in6 nativeAddr6;
+ nativeAddr6.sin6_len = sizeof(struct sockaddr_in6);
+ nativeAddr6.sin6_family = AF_INET6;
+ nativeAddr6.sin6_port = htons(port);
+ nativeAddr6.sin6_flowinfo = 0;
+ nativeAddr6.sin6_addr = in6addr_loopback;
+ nativeAddr6.sin6_scope_id = 0;
+
+ // Wrap the native address structures for CFSocketSetAddress.
+ if(address4) *address4 = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)];
+ if(address6) *address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
+
+ return 0;
+ }
+ else
+ {
+ NSString *portStr = [NSString stringWithFormat:@"%hu", port];
+
+ @synchronized (getaddrinfoLock)
+ {
+ struct addrinfo hints, *res, *res0;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ // No passive flag on a send or connect
+
+ int error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0);
+
+ if(error) return error;
+
+ for(res = res0; res; res = res->ai_next)
+ {
+ if(address4 && !*address4 && (res->ai_family == AF_INET))
+ {
+ // Found IPv4 address
+ // Wrap the native address structures for CFSocketSetAddress.
+ if(address4) *address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
+ }
+ else if(address6 && !*address6 && (res->ai_family == AF_INET6))
+ {
+ // Found IPv6 address
+ // Wrap the native address structures for CFSocketSetAddress.
+ if(address6) *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
+ }
+ }
+ freeaddrinfo(res0);
+ }
+
+ return 0;
+ }
+}
+
+- (NSString *)addressHost4:(struct sockaddr_in *)pSockaddr4
+{
+ char addrBuf[INET_ADDRSTRLEN];
+
+ if(inet_ntop(AF_INET, &pSockaddr4->sin_addr, addrBuf, sizeof(addrBuf)) == NULL)
+ {
+ [NSException raise:NSInternalInconsistencyException format:@"Cannot convert address to string."];
+ }
+
+ return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding];
+}
+
+- (NSString *)addressHost6:(struct sockaddr_in6 *)pSockaddr6
+{
+ char addrBuf[INET6_ADDRSTRLEN];
+
+ if(inet_ntop(AF_INET6, &pSockaddr6->sin6_addr, addrBuf, sizeof(addrBuf)) == NULL)
+ {
+ [NSException raise:NSInternalInconsistencyException format:@"Cannot convert address to string."];
+ }
+
+ return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding];
+}
+
+- (NSString *)addressHost:(struct sockaddr *)pSockaddr
+{
+ if(pSockaddr->sa_family == AF_INET)
+ {
+ return [self addressHost4:(struct sockaddr_in *)pSockaddr];
+ }
+ else
+ {
+ return [self addressHost6:(struct sockaddr_in6 *)pSockaddr];
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Socket Implementation:
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Binds the underlying socket(s) to the given port.
+ * The socket(s) will be able to receive data on any interface.
+ *
+ * On success, returns YES.
+ * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr.
+**/
+- (BOOL)bindToPort:(UInt16)port error:(NSError **)errPtr
+{
+ return [self bindToAddress:nil port:port error:errPtr];
+}
+
+/**
+ * Binds the underlying socket(s) to the given address and port.
+ * The sockets(s) will be able to receive data only on the given interface.
+ *
+ * To receive data on any interface, pass nil or "".
+ * To receive data only on the loopback interface, pass "localhost" or "loopback".
+ *
+ * On success, returns YES.
+ * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr.
+**/
+- (BOOL)bindToAddress:(NSString *)host port:(UInt16)port error:(NSError **)errPtr
+{
+ if(theFlags & kDidClose)
+ {
+ NSString *message = @"The socket is closed.";
+ [NSException raise:AsyncUdpSocketException format:message];
+ }
+ if(theFlags & kDidBind)
+ {
+ NSString *message = @"Cannot bind a socket more than once.";
+ [NSException raise:AsyncUdpSocketException format:message];
+ }
+ if(theFlags & kDidConnect)
+ {
+ NSString *message = @"Cannot bind after connecting. If needed, bind first, then connect.";
+ [NSException raise:AsyncUdpSocketException format:message];
+ }
+
+ // Convert the given host/port into native address structures for CFSocketSetAddress
+ NSData *address4 = nil, *address6 = nil;
+
+ int gai_error = [self convertForBindHost:host port:port intoAddress4:&address4 address6:&address6];
+ if(gai_error)
+ {
+ if(errPtr)
+ {
+ NSString *errMsg = [NSString stringWithCString:gai_strerror(gai_error) encoding:NSASCIIStringEncoding];
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:gai_error userInfo:info];
+ }
+ return NO;
+ }
+
+ NSAssert((address4 || address6), @"address4 and address6 are nil");
+
+ // Set the SO_REUSEADDR flags
+
+ int reuseOn = 1;
+ if (theSocket4) setsockopt(CFSocketGetNative(theSocket4), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
+ if (theSocket6) setsockopt(CFSocketGetNative(theSocket6), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
+
+ // Bind the sockets
+
+ if(address4)
+ {
+ if(theSocket4)
+ {
+ CFSocketError error = CFSocketSetAddress(theSocket4, (CFDataRef)address4);
+ if(error != kCFSocketSuccess)
+ {
+ if(errPtr) *errPtr = [self getSocketError];
+ return NO;
+ }
+
+ if(!address6)
+ {
+ // Using IPv4 only
+ [self closeSocket6];
+ }
+ }
+ else if(!address6)
+ {
+ if(errPtr) *errPtr = [self getIPv4UnavailableError];
+ return NO;
+ }
+ }
+
+ if(address6)
+ {
+ // Note: The iPhone doesn't currently support IPv6
+
+ if(theSocket6)
+ {
+ CFSocketError error = CFSocketSetAddress(theSocket6, (CFDataRef)address6);
+ if(error != kCFSocketSuccess)
+ {
+ if(errPtr) *errPtr = [self getSocketError];
+ return NO;
+ }
+
+ if(!address4)
+ {
+ // Using IPv6 only
+ [self closeSocket4];
+ }
+ }
+ else if(!address4)
+ {
+ if(errPtr) *errPtr = [self getIPv6UnavailableError];
+ return NO;
+ }
+ }
+
+ theFlags |= kDidBind;
+ return YES;
+}
+
+/**
+ * Connects the underlying UDP socket to the given host and port.
+ * If an IPv4 address is resolved, the IPv4 socket is connected, and the IPv6 socket is invalidated and released.
+ * If an IPv6 address is resolved, the IPv6 socket is connected, and the IPv4 socket is invalidated and released.
+ *
+ * On success, returns YES.
+ * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr.
+**/
+- (BOOL)connectToHost:(NSString *)host onPort:(UInt16)port error:(NSError **)errPtr
+{
+ if(theFlags & kDidClose)
+ {
+ NSString *message = @"The socket is closed.";
+ [NSException raise:AsyncUdpSocketException format:message];
+ }
+ if(theFlags & kDidConnect)
+ {
+ NSString *message = @"Cannot connect a socket more than once.";
+ [NSException raise:AsyncUdpSocketException format:message];
+ }
+
+ // Convert the given host/port into native address structures for CFSocketSetAddress
+ NSData *address4 = nil, *address6 = nil;
+
+ int error = [self convertForSendHost:host port:port intoAddress4:&address4 address6:&address6];
+ if(error)
+ {
+ if(errPtr)
+ {
+ NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding];
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info];
+ }
+ return NO;
+ }
+
+ NSAssert((address4 || address6), @"address4 and address6 are nil");
+
+ // We only want to connect via a single interface.
+ // IPv4 is currently preferred, but this may change in the future.
+
+ if(address4)
+ {
+ if(theSocket4)
+ {
+ CFSocketError sockErr = CFSocketConnectToAddress(theSocket4, (CFDataRef)address4, (CFTimeInterval)0.0);
+ if(sockErr != kCFSocketSuccess)
+ {
+ if(errPtr) *errPtr = [self getSocketError];
+ return NO;
+ }
+ theFlags |= kDidConnect;
+
+ // We're connected to an IPv4 address, so no need for the IPv6 socket
+ [self closeSocket6];
+
+ return YES;
+ }
+ else if(!address6)
+ {
+ if(errPtr) *errPtr = [self getIPv4UnavailableError];
+ return NO;
+ }
+ }
+
+ if(address6)
+ {
+ // Note: The iPhone doesn't currently support IPv6
+
+ if(theSocket6)
+ {
+ CFSocketError sockErr = CFSocketConnectToAddress(theSocket6, (CFDataRef)address6, (CFTimeInterval)0.0);
+ if(sockErr != kCFSocketSuccess)
+ {
+ if(errPtr) *errPtr = [self getSocketError];
+ return NO;
+ }
+ theFlags |= kDidConnect;
+
+ // We're connected to an IPv6 address, so no need for the IPv4 socket
+ [self closeSocket4];
+
+ return YES;
+ }
+ else
+ {
+ if(errPtr) *errPtr = [self getIPv6UnavailableError];
+ return NO;
+ }
+ }
+
+ // It shouldn't be possible to get to this point because either address4 or address6 was non-nil.
+ if(errPtr) *errPtr = nil;
+ return NO;
+}
+
+/**
+ * Connects the underlying UDP socket to the remote address.
+ * If the address is an IPv4 address, the IPv4 socket is connected, and the IPv6 socket is invalidated and released.
+ * If the address is an IPv6 address, the IPv6 socket is connected, and the IPv4 socket is invalidated and released.
+ *
+ * The address is a native address structure, as may be returned from API's such as Bonjour.
+ * An address may be created manually by simply wrapping a sockaddr_in or sockaddr_in6 in an NSData object.
+ *
+ * On success, returns YES.
+ * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr.
+**/
+- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr
+{
+ if(theFlags & kDidClose)
+ {
+ NSString *message = @"The socket is closed.";
+ [NSException raise:AsyncUdpSocketException format:message];
+ }
+ if(theFlags & kDidConnect)
+ {
+ NSString *message = @"Cannot connect a socket more than once.";
+ [NSException raise:AsyncUdpSocketException format:message];
+ }
+
+ // Is remoteAddr an IPv4 address?
+ if([remoteAddr length] == sizeof(struct sockaddr_in))
+ {
+ if(theSocket4)
+ {
+ CFSocketError error = CFSocketConnectToAddress(theSocket4, (CFDataRef)remoteAddr, (CFTimeInterval)0.0);
+ if(error != kCFSocketSuccess)
+ {
+ if(errPtr) *errPtr = [self getSocketError];
+ return NO;
+ }
+ theFlags |= kDidConnect;
+
+ // We're connected to an IPv4 address, so no need for the IPv6 socket
+ [self closeSocket6];
+
+ return YES;
+ }
+ else
+ {
+ if(errPtr) *errPtr = [self getIPv4UnavailableError];
+ return NO;
+ }
+ }
+
+ // Is remoteAddr an IPv6 address?
+ if([remoteAddr length] == sizeof(struct sockaddr_in6))
+ {
+ if(theSocket6)
+ {
+ CFSocketError error = CFSocketConnectToAddress(theSocket6, (CFDataRef)remoteAddr, (CFTimeInterval)0.0);
+ if(error != kCFSocketSuccess)
+ {
+ if(errPtr) *errPtr = [self getSocketError];
+ return NO;
+ }
+ theFlags |= kDidConnect;
+
+ // We're connected to an IPv6 address, so no need for the IPv4 socket
+ [self closeSocket4];
+
+ return YES;
+ }
+ else
+ {
+ if(errPtr) *errPtr = [self getIPv6UnavailableError];
+ return NO;
+ }
+ }
+
+ // The remoteAddr was invalid
+ if(errPtr)
+ {
+ NSString *errMsg = @"remoteAddr parameter is not a valid address";
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ *errPtr = [NSError errorWithDomain:AsyncUdpSocketErrorDomain
+ code:AsyncUdpSocketBadParameter
+ userInfo:info];
+ }
+ return NO;
+}
+
+/**
+ * Join multicast group
+ *
+ * Group should be a multicast IP address (eg. @"239.255.250.250" for IPv4).
+ * Address is local interface for IPv4, but currently defaults under IPv6.
+**/
+- (BOOL)joinMulticastGroup:(NSString *)group error:(NSError **)errPtr
+{
+ return [self joinMulticastGroup:group withAddress:nil error:errPtr];
+}
+
+- (BOOL)joinMulticastGroup:(NSString *)group withAddress:(NSString *)address error:(NSError **)errPtr
+{
+ if(theFlags & kDidClose)
+ {
+ NSString *message = @"The socket is closed.";
+ [NSException raise:AsyncUdpSocketException format:message];
+ }
+ if(!(theFlags & kDidBind))
+ {
+ NSString *message = @"Must bind a socket before joining a multicast group.";
+ [NSException raise:AsyncUdpSocketException format:message];
+ }
+ if(theFlags & kDidConnect)
+ {
+ NSString *message = @"Cannot join a multicast group if connected.";
+ [NSException raise:AsyncUdpSocketException format:message];
+ }
+
+ // Get local interface address
+ // Convert the given host/port into native address structures for CFSocketSetAddress
+ NSData *address4 = nil, *address6 = nil;
+
+ int error = [self convertForBindHost:address port:0 intoAddress4:&address4 address6:&address6];
+ if(error)
+ {
+ if(errPtr)
+ {
+ NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding];
+ NSString *errDsc = [NSString stringWithFormat:@"Invalid parameter 'address': %@", errMsg];
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errDsc forKey:NSLocalizedDescriptionKey];
+
+ *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info];
+ }
+ return NO;
+ }
+
+ NSAssert((address4 || address6), @"address4 and address6 are nil");
+
+ // Get multicast address (group)
+ NSData *group4 = nil, *group6 = nil;
+
+ error = [self convertForBindHost:group port:0 intoAddress4:&group4 address6:&group6];
+ if(error)
+ {
+ if(errPtr)
+ {
+ NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding];
+ NSString *errDsc = [NSString stringWithFormat:@"Invalid parameter 'group': %@", errMsg];
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errDsc forKey:NSLocalizedDescriptionKey];
+
+ *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info];
+ }
+ return NO;
+ }
+
+ NSAssert((group4 || group6), @"group4 and group6 are nil");
+
+ if(theSocket4 && group4 && address4)
+ {
+ const struct sockaddr_in* nativeAddress = [address4 bytes];
+ const struct sockaddr_in* nativeGroup = [group4 bytes];
+
+ struct ip_mreq imreq;
+ imreq.imr_multiaddr = nativeGroup->sin_addr;
+ imreq.imr_interface = nativeAddress->sin_addr;
+
+ // JOIN multicast group on default interface
+ error = setsockopt(CFSocketGetNative(theSocket4), IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (const void *)&imreq, sizeof(struct ip_mreq));
+ if(error)
+ {
+ if(errPtr)
+ {
+ NSString *errMsg = @"Unable to join IPv4 multicast group";
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainPOSIX" code:error userInfo:info];
+ }
+ return NO;
+ }
+
+ // Using IPv4 only
+ [self closeSocket6];
+
+ return YES;
+ }
+
+ if(theSocket6 && group6 && address6)
+ {
+ const struct sockaddr_in6* nativeGroup = [group6 bytes];
+
+ struct ipv6_mreq imreq;
+ imreq.ipv6mr_multiaddr = nativeGroup->sin6_addr;
+ imreq.ipv6mr_interface = 0;
+
+ // JOIN multicast group on default interface
+ error = setsockopt(CFSocketGetNative(theSocket6), IPPROTO_IP, IPV6_JOIN_GROUP,
+ (const void *)&imreq, sizeof(struct ipv6_mreq));
+ if(error)
+ {
+ if(errPtr)
+ {
+ NSString *errMsg = @"Unable to join IPv6 multicast group";
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainPOSIX" code:error userInfo:info];
+ }
+ return NO;
+ }
+
+ // Using IPv6 only
+ [self closeSocket4];
+
+ return YES;
+ }
+
+ // The given address and group didn't match the existing socket(s).
+ // This means there were no compatible combination of all IPv4 or IPv6 socket, group and address.
+ if(errPtr)
+ {
+ NSString *errMsg = @"Invalid group and/or address, not matching existing socket(s)";
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ *errPtr = [NSError errorWithDomain:AsyncUdpSocketErrorDomain
+ code:AsyncUdpSocketBadParameter
+ userInfo:info];
+ }
+ return NO;
+}
+
+/**
+ * By default, the underlying socket in the OS will not allow you to send broadcast messages.
+ * In order to send broadcast messages, you need to enable this functionality in the socket.
+ *
+ * A broadcast is a UDP message to addresses like "192.168.255.255" or "255.255.255.255" that is
+ * delivered to every host on the network.
+ * The reason this is generally disabled by default is to prevent
+ * accidental broadcast messages from flooding the network.
+**/
+- (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr
+{
+ if (theSocket4)
+ {
+ int value = flag ? 1 : 0;
+ int error = setsockopt(CFSocketGetNative(theSocket4), SOL_SOCKET, SO_BROADCAST,
+ (const void *)&value, sizeof(value));
+ if(error)
+ {
+ if(errPtr)
+ {
+ NSString *errMsg = @"Unable to enable broadcast message sending";
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainPOSIX" code:error userInfo:info];
+ }
+ return NO;
+ }
+ }
+
+ // IPv6 does not implement broadcast, the ability to send a packet to all hosts on the attached link.
+ // The same effect can be achieved by sending a packet to the link-local all hosts multicast group.
+
+ return YES;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Disconnect Implementation:
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)emptyQueues
+{
+ if (theCurrentSend) [self endCurrentSend];
+ if (theCurrentReceive) [self endCurrentReceive];
+
+ [theSendQueue removeAllObjects];
+ [theReceiveQueue removeAllObjects];
+
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueSend) object:nil];
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueReceive) object:nil];
+
+ theFlags &= ~kDequeueSendScheduled;
+ theFlags &= ~kDequeueReceiveScheduled;
+}
+
+- (void)closeSocket4
+{
+ if (theSocket4 != NULL)
+ {
+ CFSocketInvalidate(theSocket4);
+ CFRelease(theSocket4);
+ theSocket4 = NULL;
+ }
+ if (theSource4 != NULL)
+ {
+ [self runLoopRemoveSource:theSource4];
+ CFRelease(theSource4);
+ theSource4 = NULL;
+ }
+}
+
+- (void)closeSocket6
+{
+ if (theSocket6 != NULL)
+ {
+ CFSocketInvalidate(theSocket6);
+ CFRelease(theSocket6);
+ theSocket6 = NULL;
+ }
+ if (theSource6 != NULL)
+ {
+ [self runLoopRemoveSource:theSource6];
+ CFRelease(theSource6);
+ theSource6 = NULL;
+ }
+}
+
+- (void)close
+{
+ [self emptyQueues];
+ [self closeSocket4];
+ [self closeSocket6];
+
+ theRunLoop = NULL;
+
+ // Delay notification to give user freedom to release without returning here and core-dumping.
+ if ([theDelegate respondsToSelector:@selector(onUdpSocketDidClose:)])
+ {
+ [theDelegate performSelector:@selector(onUdpSocketDidClose:)
+ withObject:self
+ afterDelay:0
+ inModes:theRunLoopModes];
+ }
+
+ theFlags |= kDidClose;
+}
+
+- (void)closeAfterSending
+{
+ if(theFlags & kDidClose) return;
+
+ theFlags |= (kForbidSendReceive | kCloseAfterSends);
+ [self maybeScheduleClose];
+}
+
+- (void)closeAfterReceiving
+{
+ if(theFlags & kDidClose) return;
+
+ theFlags |= (kForbidSendReceive | kCloseAfterReceives);
+ [self maybeScheduleClose];
+}
+
+- (void)closeAfterSendingAndReceiving
+{
+ if(theFlags & kDidClose) return;
+
+ theFlags |= (kForbidSendReceive | kCloseAfterSends | kCloseAfterReceives);
+ [self maybeScheduleClose];
+}
+
+- (void)maybeScheduleClose
+{
+ BOOL shouldDisconnect = NO;
+
+ if(theFlags & kCloseAfterSends)
+ {
+ if(([theSendQueue count] == 0) && (theCurrentSend == nil))
+ {
+ if(theFlags & kCloseAfterReceives)
+ {
+ if(([theReceiveQueue count] == 0) && (theCurrentReceive == nil))
+ {
+ shouldDisconnect = YES;
+ }
+ }
+ else
+ {
+ shouldDisconnect = YES;
+ }
+ }
+ }
+ else if(theFlags & kCloseAfterReceives)
+ {
+ if(([theReceiveQueue count] == 0) && (theCurrentReceive == nil))
+ {
+ shouldDisconnect = YES;
+ }
+ }
+
+ if(shouldDisconnect)
+ {
+ [self performSelector:@selector(close) withObject:nil afterDelay:0 inModes:theRunLoopModes];
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Errors
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Returns a standard error object for the current errno value.
+ * Errno is used for low-level BSD socket errors.
+**/
+- (NSError *)getErrnoError
+{
+ NSString *errorMsg = [NSString stringWithUTF8String:strerror(errno)];
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errorMsg forKey:NSLocalizedDescriptionKey];
+
+ return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo];
+}
+
+/**
+ * Returns a standard error message for a CFSocket error.
+ * Unfortunately, CFSocket offers no feedback on its errors.
+**/
+- (NSError *)getSocketError
+{
+ NSString *errMsg = @"General CFSocket error";
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketCFSocketError userInfo:info];
+}
+
+- (NSError *)getIPv4UnavailableError
+{
+ NSString *errMsg = @"IPv4 is unavailable due to binding/connecting using IPv6 only";
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketIPv4Unavailable userInfo:info];
+}
+
+- (NSError *)getIPv6UnavailableError
+{
+ NSString *errMsg = @"IPv6 is unavailable due to binding/connecting using IPv4 only or is not supported on this platform";
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketIPv6Unavailable userInfo:info];
+}
+
+- (NSError *)getSendTimeoutError
+{
+ NSString *errMsg = @"Send operation timed out";
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketSendTimeoutError userInfo:info];
+}
+- (NSError *)getReceiveTimeoutError
+{
+ NSString *errMsg = @"Receive operation timed out";
+ NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
+
+ return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketReceiveTimeoutError userInfo:info];
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Diagnostics
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (NSString *)localHost
+{
+ if(cachedLocalHost) return cachedLocalHost;
+
+ if(theSocket4)
+ return [self localHost:theSocket4];
+ else
+ return [self localHost:theSocket6];
+}
+
+- (UInt16)localPort
+{
+ if(cachedLocalPort > 0) return cachedLocalPort;
+
+ if(theSocket4)
+ return [self localPort:theSocket4];
+ else
+ return [self localPort:theSocket6];
+}
+
+- (NSString *)connectedHost
+{
+ if(cachedConnectedHost) return cachedConnectedHost;
+
+ if(theSocket4)
+ return [self connectedHost:theSocket4];
+ else
+ return [self connectedHost:theSocket6];
+}
+
+- (UInt16)connectedPort
+{
+ if(cachedConnectedPort > 0) return cachedConnectedPort;
+
+ if(theSocket4)
+ return [self connectedPort:theSocket4];
+ else
+ return [self connectedPort:theSocket6];
+}
+
+- (NSString *)localHost:(CFSocketRef)theSocket
+{
+ if(theSocket == NULL) return nil;
+
+ // Unfortunately we can't use CFSocketCopyAddress.
+ // The CFSocket library caches the address the first time you call CFSocketCopyAddress.
+ // So if this is called prior to binding/connecting/sending, it won't be updated again when necessary,
+ // and will continue to return the old value of the socket address.
+
+ NSString *result = nil;
+
+ if(theSocket == theSocket4)
+ {
+ struct sockaddr_in sockaddr4;
+ socklen_t sockaddr4len = sizeof(sockaddr4);
+
+ if(getsockname(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0)
+ {
+ return nil;
+ }
+ result = [self addressHost4:&sockaddr4];
+ }
+ else
+ {
+ struct sockaddr_in6 sockaddr6;
+ socklen_t sockaddr6len = sizeof(sockaddr6);
+
+ if(getsockname(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0)
+ {
+ return nil;
+ }
+ result = [self addressHost6:&sockaddr6];
+ }
+
+ if(theFlags & kDidBind)
+ {
+ [cachedLocalHost release];
+ cachedLocalHost = [result copy];
+ }
+
+ return result;
+}
+
+- (UInt16)localPort:(CFSocketRef)theSocket
+{
+ if(theSocket == NULL) return 0;
+
+ // Unfortunately we can't use CFSocketCopyAddress.
+ // The CFSocket library caches the address the first time you call CFSocketCopyAddress.
+ // So if this is called prior to binding/connecting/sending, it won't be updated again when necessary,
+ // and will continue to return the old value of the socket address.
+
+ UInt16 result = 0;
+
+ if(theSocket == theSocket4)
+ {
+ struct sockaddr_in sockaddr4;
+ socklen_t sockaddr4len = sizeof(sockaddr4);
+
+ if(getsockname(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0)
+ {
+ return 0;
+ }
+ result = ntohs(sockaddr4.sin_port);
+ }
+ else
+ {
+ struct sockaddr_in6 sockaddr6;
+ socklen_t sockaddr6len = sizeof(sockaddr6);
+
+ if(getsockname(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0)
+ {
+ return 0;
+ }
+ result = ntohs(sockaddr6.sin6_port);
+ }
+
+ if(theFlags & kDidBind)
+ {
+ cachedLocalPort = result;
+ }
+
+ return result;
+}
+
+- (NSString *)connectedHost:(CFSocketRef)theSocket
+{
+ if(theSocket == NULL) return nil;
+
+ // Unfortunately we can't use CFSocketCopyPeerAddress.
+ // The CFSocket library caches the address the first time you call CFSocketCopyPeerAddress.
+ // So if this is called prior to binding/connecting/sending, it may not be updated again when necessary,
+ // and will continue to return the old value of the socket peer address.
+
+ NSString *result = nil;
+
+ if(theSocket == theSocket4)
+ {
+ struct sockaddr_in sockaddr4;
+ socklen_t sockaddr4len = sizeof(sockaddr4);
+
+ if(getpeername(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0)
+ {
+ return nil;
+ }
+ result = [self addressHost4:&sockaddr4];
+ }
+ else
+ {
+ struct sockaddr_in6 sockaddr6;
+ socklen_t sockaddr6len = sizeof(sockaddr6);
+
+ if(getpeername(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0)
+ {
+ return nil;
+ }
+ result = [self addressHost6:&sockaddr6];
+ }
+
+ if(theFlags & kDidConnect)
+ {
+ [cachedConnectedHost release];
+ cachedConnectedHost = [result copy];
+ }
+
+ return result;
+}
+
+- (UInt16)connectedPort:(CFSocketRef)theSocket
+{
+ if(theSocket == NULL) return 0;
+
+ // Unfortunately we can't use CFSocketCopyPeerAddress.
+ // The CFSocket library caches the address the first time you call CFSocketCopyPeerAddress.
+ // So if this is called prior to binding/connecting/sending, it may not be updated again when necessary,
+ // and will continue to return the old value of the socket peer address.
+
+ UInt16 result = 0;
+
+ if(theSocket == theSocket4)
+ {
+ struct sockaddr_in sockaddr4;
+ socklen_t sockaddr4len = sizeof(sockaddr4);
+
+ if(getpeername(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0)
+ {
+ return 0;
+ }
+ result = ntohs(sockaddr4.sin_port);
+ }
+ else
+ {
+ struct sockaddr_in6 sockaddr6;
+ socklen_t sockaddr6len = sizeof(sockaddr6);
+
+ if(getpeername(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0)
+ {
+ return 0;
+ }
+ result = ntohs(sockaddr6.sin6_port);
+ }
+
+ if(theFlags & kDidConnect)
+ {
+ cachedConnectedPort = result;
+ }
+
+ return result;
+}
+
+- (BOOL)isConnected
+{
+ return (((theFlags & kDidConnect) != 0) && ((theFlags & kDidClose) == 0));
+}
+
+- (BOOL)isConnectedToHost:(NSString *)host port:(UInt16)port
+{
+ return [[self connectedHost] isEqualToString:host] && ([self connectedPort] == port);
+}
+
+- (BOOL)isClosed
+{
+ return (theFlags & kDidClose);
+}
+
+- (BOOL)isIPv4
+{
+ return (theSocket4 != NULL);
+}
+
+- (BOOL)isIPv6
+{
+ return (theSocket6 != NULL);
+}
+
+- (unsigned int)maximumTransmissionUnit
+{
+ CFSocketNativeHandle theNativeSocket;
+ if(theSocket4)
+ theNativeSocket = CFSocketGetNative(theSocket4);
+ else if(theSocket6)
+ theNativeSocket = CFSocketGetNative(theSocket6);
+ else
+ return 0;
+
+ if(theNativeSocket == 0)
+ {
+ return 0;
+ }
+
+ struct ifreq ifr;
+ bzero(&ifr, sizeof(ifr));
+
+ if(if_indextoname(theNativeSocket, ifr.ifr_name) == NULL)
+ {
+ return 0;
+ }
+
+ if(ioctl(theNativeSocket, SIOCGIFMTU, &ifr) >= 0)
+ {
+ return ifr.ifr_mtu;
+ }
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Sending
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (BOOL)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
+{
+ if((data == nil) || ([data length] == 0)) return NO;
+ if(theFlags & kForbidSendReceive) return NO;
+ if(theFlags & kDidClose) return NO;
+
+ // This method is only for connected sockets
+ if(![self isConnected]) return NO;
+
+ AsyncSendPacket *packet = [[AsyncSendPacket alloc] initWithData:data address:nil timeout:timeout tag:tag];
+
+ [theSendQueue addObject:packet];
+ [self scheduleDequeueSend];
+
+ [packet release];
+ return YES;
+}
+
+- (BOOL)sendData:(NSData *)data toHost:(NSString *)host port:(UInt16)port withTimeout:(NSTimeInterval)timeout tag:(long)tag
+{
+ if((data == nil) || ([data length] == 0)) return NO;
+ if(theFlags & kForbidSendReceive) return NO;
+ if(theFlags & kDidClose) return NO;
+
+ // This method is only for non-connected sockets
+ if([self isConnected]) return NO;
+
+ NSData *address4 = nil, *address6 = nil;
+ [self convertForSendHost:host port:port intoAddress4:&address4 address6:&address6];
+
+ AsyncSendPacket *packet = nil;
+
+ if(address4 && theSocket4)
+ packet = [[AsyncSendPacket alloc] initWithData:data address:address4 timeout:timeout tag:tag];
+ else if(address6 && theSocket6)
+ packet = [[AsyncSendPacket alloc] initWithData:data address:address6 timeout:timeout tag:tag];
+ else
+ return NO;
+
+ [theSendQueue addObject:packet];
+ [self scheduleDequeueSend];
+
+ [packet release];
+ return YES;
+}
+
+- (BOOL)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag
+{
+ if((data == nil) || ([data length] == 0)) return NO;
+ if(theFlags & kForbidSendReceive) return NO;
+ if(theFlags & kDidClose) return NO;
+
+ // This method is only for non-connected sockets
+ if([self isConnected]) return NO;
+
+ if([remoteAddr length] == sizeof(struct sockaddr_in) && !theSocket4)
+ return NO;
+
+ if([remoteAddr length] == sizeof(struct sockaddr_in6) && !theSocket6)
+ return NO;
+
+ AsyncSendPacket *packet = [[AsyncSendPacket alloc] initWithData:data address:remoteAddr timeout:timeout tag:tag];
+
+ [theSendQueue addObject:packet];
+ [self scheduleDequeueSend];
+
+ [packet release];
+ return YES;
+}
+
+- (BOOL)canAcceptBytes:(CFSocketRef)sockRef
+{
+ if(sockRef == theSocket4)
+ {
+ if(theFlags & kSock4CanAcceptBytes) return YES;
+ }
+ else
+ {
+ if(theFlags & kSock6CanAcceptBytes) return YES;
+ }
+
+ CFSocketNativeHandle theNativeSocket = CFSocketGetNative(sockRef);
+
+ if(theNativeSocket == 0)
+ {
+ NSLog(@"Error - Could not get CFSocketNativeHandle from CFSocketRef");
+ return NO;
+ }
+
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(theNativeSocket, &fds);
+
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ return select(FD_SETSIZE, NULL, &fds, NULL, &timeout) > 0;
+}
+
+- (CFSocketRef)socketForPacket:(AsyncSendPacket *)packet
+{
+ if(!theSocket4)
+ return theSocket6;
+ if(!theSocket6)
+ return theSocket4;
+
+ return ([packet->address length] == sizeof(struct sockaddr_in)) ? theSocket4 : theSocket6;
+}
+
+/**
+ * Puts a maybeDequeueSend on the run loop.
+**/
+- (void)scheduleDequeueSend
+{
+ if((theFlags & kDequeueSendScheduled) == 0)
+ {
+ theFlags |= kDequeueSendScheduled;
+ [self performSelector:@selector(maybeDequeueSend) withObject:nil afterDelay:0 inModes:theRunLoopModes];
+ }
+}
+
+/**
+ * This method starts a new send, if needed.
+ * It is called when a user requests a send.
+**/
+- (void)maybeDequeueSend
+{
+ // Unset the flag indicating a call to this method is scheduled
+ theFlags &= ~kDequeueSendScheduled;
+
+ if(theCurrentSend == nil)
+ {
+ if([theSendQueue count] > 0)
+ {
+ // Dequeue next send packet
+ theCurrentSend = [[theSendQueue objectAtIndex:0] retain];
+ [theSendQueue removeObjectAtIndex:0];
+
+ // Start time-out timer.
+ if(theCurrentSend->timeout >= 0.0)
+ {
+ theSendTimer = [NSTimer timerWithTimeInterval:theCurrentSend->timeout
+ target:self
+ selector:@selector(doSendTimeout:)
+ userInfo:nil
+ repeats:NO];
+
+ [self runLoopAddTimer:theSendTimer];
+ }
+
+ // Immediately send, if possible.
+ [self doSend:[self socketForPacket:theCurrentSend]];
+ }
+ else if(theFlags & kCloseAfterSends)
+ {
+ if(theFlags & kCloseAfterReceives)
+ {
+ if(([theReceiveQueue count] == 0) && (theCurrentReceive == nil))
+ {
+ [self close];
+ }
+ }
+ else
+ {
+ [self close];
+ }
+ }
+ }
+}
+
+/**
+ * This method is called when a new read is taken from the read queue or when new data becomes available on the stream.
+**/
+- (void)doSend:(CFSocketRef)theSocket
+{
+ if(theCurrentSend != nil)
+ {
+ if(theSocket != [self socketForPacket:theCurrentSend])
+ {
+ // Current send is for the other socket
+ return;
+ }
+
+ if([self canAcceptBytes:theSocket])
+ {
+ int result;
+ CFSocketNativeHandle theNativeSocket = CFSocketGetNative(theSocket);
+
+ const void *buf = [theCurrentSend->buffer bytes];
+ unsigned bufSize = [theCurrentSend->buffer length];
+
+ if([self isConnected])
+ {
+ result = send(theNativeSocket, buf, bufSize, 0);
+ }
+ else
+ {
+ const void *dst = [theCurrentSend->address bytes];
+ unsigned dstSize = [theCurrentSend->address length];
+
+ result = sendto(theNativeSocket, buf, bufSize, 0, dst, dstSize);
+ }
+
+ if(theSocket == theSocket4)
+ theFlags &= ~kSock4CanAcceptBytes;
+ else
+ theFlags &= ~kSock6CanAcceptBytes;
+
+ if(result < 0)
+ {
+ [self failCurrentSend:[self getErrnoError]];
+ }
+ else
+ {
+ // If it wasn't bound before, it's bound now
+ theFlags |= kDidBind;
+
+ [self completeCurrentSend];
+ }
+
+ [self scheduleDequeueSend];
+ }
+ else
+ {
+ // Request notification when the socket is ready to send more data
+ CFSocketEnableCallBacks(theSocket, kCFSocketReadCallBack | kCFSocketWriteCallBack);
+ }
+ }
+}
+
+- (void)completeCurrentSend
+{
+ NSAssert (theCurrentSend, @"Trying to complete current send when there is no current send.");
+
+ if ([theDelegate respondsToSelector:@selector(onUdpSocket:didSendDataWithTag:)])
+ {
+ [theDelegate onUdpSocket:self didSendDataWithTag:theCurrentSend->tag];
+ }
+
+ if (theCurrentSend != nil) [self endCurrentSend]; // Caller may have disconnected.
+}
+
+- (void)failCurrentSend:(NSError *)error
+{
+ NSAssert (theCurrentSend, @"Trying to fail current send when there is no current send.");
+
+ if ([theDelegate respondsToSelector:@selector(onUdpSocket:didNotSendDataWithTag:dueToError:)])
+ {
+ [theDelegate onUdpSocket:self didNotSendDataWithTag:theCurrentSend->tag dueToError:error];
+ }
+
+ if (theCurrentSend != nil) [self endCurrentSend]; // Caller may have disconnected.
+}
+
+/**
+ * Ends the current send, and all associated variables such as the send timer.
+**/
+- (void)endCurrentSend
+{
+ NSAssert (theCurrentSend, @"Trying to end current send when there is no current send.");
+
+ [theSendTimer invalidate];
+ theSendTimer = nil;
+
+ [theCurrentSend release];
+ theCurrentSend = nil;
+}
+
+- (void)doSendTimeout:(NSTimer *)timer
+{
+ if (timer != theSendTimer) return; // Old timer. Ignore it.
+ if (theCurrentSend != nil)
+ {
+ [self failCurrentSend:[self getSendTimeoutError]];
+ [self scheduleDequeueSend];
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark Receiving
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)receiveWithTimeout:(NSTimeInterval)timeout tag:(long)tag
+{
+ if(theFlags & kForbidSendReceive) return;
+ if(theFlags & kDidClose) return;
+
+ AsyncReceivePacket *packet = [[AsyncReceivePacket alloc] initWithTimeout:timeout tag:tag];
+
+ [theReceiveQueue addObject:packet];
+ [self scheduleDequeueReceive];
+
+ [packet release];
+}
+
+- (BOOL)hasBytesAvailable:(CFSocketRef)sockRef
+{
+ if(sockRef == theSocket4)
+ {
+ if(theFlags & kSock4HasBytesAvailable) return YES;
+ }
+ else
+ {
+ if(theFlags & kSock6HasBytesAvailable) return YES;
+ }
+
+ CFSocketNativeHandle theNativeSocket = CFSocketGetNative(sockRef);
+
+ if(theNativeSocket == 0)
+ {
+ NSLog(@"Error - Could not get CFSocketNativeHandle from CFSocketRef");
+ return NO;
+ }
+
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(theNativeSocket, &fds);
+
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ return select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0;
+}
+
+/**
+ * Puts a maybeDequeueReceive on the run loop.
+**/
+- (void)scheduleDequeueReceive
+{
+ if((theFlags & kDequeueReceiveScheduled) == 0)
+ {
+ theFlags |= kDequeueReceiveScheduled;
+ [self performSelector:@selector(maybeDequeueReceive) withObject:nil afterDelay:0 inModes:theRunLoopModes];
+ }
+}
+
+/**
+ * Starts a new receive operation if needed
+**/
+- (void)maybeDequeueReceive
+{
+ // Unset the flag indicating a call to this method is scheduled
+ theFlags &= ~kDequeueReceiveScheduled;
+
+ if (theCurrentReceive == nil)
+ {
+ if([theReceiveQueue count] > 0)
+ {
+ // Dequeue next receive packet
+ theCurrentReceive = [[theReceiveQueue objectAtIndex:0] retain];
+ [theReceiveQueue removeObjectAtIndex:0];
+
+ // Start time-out timer.
+ if (theCurrentReceive->timeout >= 0.0)
+ {
+ theReceiveTimer = [NSTimer timerWithTimeInterval:theCurrentReceive->timeout
+ target:self
+ selector:@selector(doReceiveTimeout:)
+ userInfo:nil
+ repeats:NO];
+
+ [self runLoopAddTimer:theReceiveTimer];
+ }
+
+ // Immediately receive, if possible
+ // We always check both sockets so we don't ever starve one of them.
+ // We also check them in alternating orders to prevent starvation if both of them
+ // have a continuous flow of incoming data.
+ if(theFlags & kFlipFlop)
+ {
+ [self doReceive4];
+ [self doReceive6];
+ }
+ else
+ {
+ [self doReceive6];
+ [self doReceive4];
+ }
+
+ theFlags ^= kFlipFlop;
+ }
+ else if(theFlags & kCloseAfterReceives)
+ {
+ if(theFlags & kCloseAfterSends)
+ {
+ if(([theSendQueue count] == 0) && (theCurrentSend == nil))
+ {
+ [self close];
+ }
+ }
+ else
+ {
+ [self close];
+ }
+ }
+ }
+}
+
+- (void)doReceive4
+{
+ if(theSocket4) [self doReceive:theSocket4];
+}
+
+- (void)doReceive6
+{
+ if(theSocket6) [self doReceive:theSocket6];
+}
+
+- (void)doReceive:(CFSocketRef)theSocket
+{
+ if (theCurrentReceive != nil)
+ {
+ BOOL appIgnoredReceivedData;
+ BOOL userIgnoredReceivedData;
+
+ do
+ {
+ // Set or reset ignored variables.
+ // If the app or user ignores the received data, we'll continue this do-while loop.
+ appIgnoredReceivedData = NO;
+ userIgnoredReceivedData = NO;
+
+ if([self hasBytesAvailable:theSocket])
+ {
+ int result;
+ CFSocketNativeHandle theNativeSocket = CFSocketGetNative(theSocket);
+
+ // Allocate buffer for recvfrom operation.
+ // If the operation is successful, we'll realloc the buffer to the appropriate size,
+ // and create an NSData wrapper around it without needing to copy any bytes around.
+ void *buf = malloc(maxReceiveBufferSize);
+ size_t bufSize = maxReceiveBufferSize;
+
+ if(theSocket == theSocket4)
+ {
+ struct sockaddr_in sockaddr4;
+ socklen_t sockaddr4len = sizeof(sockaddr4);
+
+ result = recvfrom(theNativeSocket, buf, bufSize, 0, (struct sockaddr *)&sockaddr4, &sockaddr4len);
+
+ if(result >= 0)
+ {
+ NSString *host = [self addressHost4:&sockaddr4];
+ UInt16 port = ntohs(sockaddr4.sin_port);
+
+ if([self isConnected] && ![self isConnectedToHost:host port:port])
+ {
+ // The user connected to an address, and the received data doesn't match the address.
+ // This may happen if the data is received by the kernel prior to the connect call.
+ appIgnoredReceivedData = YES;
+ }
+ else
+ {
+ if(result != bufSize)
+ {
+ buf = realloc(buf, result);
+ }
+ theCurrentReceive->buffer = [[NSData alloc] initWithBytesNoCopy:buf
+ length:result
+ freeWhenDone:YES];
+ theCurrentReceive->host = [host retain];
+ theCurrentReceive->port = port;
+ }
+ }
+
+ theFlags &= ~kSock4HasBytesAvailable;
+ }
+ else
+ {
+ struct sockaddr_in6 sockaddr6;
+ socklen_t sockaddr6len = sizeof(sockaddr6);
+
+ result = recvfrom(theNativeSocket, buf, bufSize, 0, (struct sockaddr *)&sockaddr6, &sockaddr6len);
+
+ if(result >= 0)
+ {
+ NSString *host = [self addressHost6:&sockaddr6];
+ UInt16 port = ntohs(sockaddr6.sin6_port);
+
+ if([self isConnected] && ![self isConnectedToHost:host port:port])
+ {
+ // The user connected to an address, and the received data doesn't match the address.
+ // This may happen if the data is received by the kernel prior to the connect call.
+ appIgnoredReceivedData = YES;
+ }
+ else
+ {
+ if(result != bufSize)
+ {
+ buf = realloc(buf, result);
+ }
+ theCurrentReceive->buffer = [[NSData alloc] initWithBytesNoCopy:buf
+ length:result
+ freeWhenDone:YES];
+ theCurrentReceive->host = [host retain];
+ theCurrentReceive->port = port;
+ }
+ }
+
+ theFlags &= ~kSock6HasBytesAvailable;
+ }
+
+ // Check to see if we need to free our alloc'd buffer
+ // If the buffer is non-nil, this means it has taken ownership of the buffer
+ if(theCurrentReceive->buffer == nil)
+ {
+ free(buf);
+ }
+
+ if(result < 0)
+ {
+ [self failCurrentReceive:[self getErrnoError]];
+ [self scheduleDequeueReceive];
+ }
+ else if(!appIgnoredReceivedData)
+ {
+ BOOL finished = [self maybeCompleteCurrentReceive];
+
+ if(finished)
+ {
+ [self scheduleDequeueReceive];
+ }
+ else
+ {
+ [theCurrentReceive->buffer release];
+ [theCurrentReceive->host release];
+
+ theCurrentReceive->buffer = nil;
+ theCurrentReceive->host = nil;
+
+ userIgnoredReceivedData = YES;
+ }
+ }
+ }
+ else
+ {
+ // Request notification when the socket is ready to receive more data
+ CFSocketEnableCallBacks(theSocket, kCFSocketReadCallBack | kCFSocketWriteCallBack);
+ }
+
+ } while(appIgnoredReceivedData || userIgnoredReceivedData);
+ }
+}
+
+- (BOOL)maybeCompleteCurrentReceive
+{
+ NSAssert (theCurrentReceive, @"Trying to complete current receive when there is no current receive.");
+
+ BOOL finished = YES;
+
+ if ([theDelegate respondsToSelector:@selector(onUdpSocket:didReceiveData:withTag:fromHost:port:)])
+ {
+ finished = [theDelegate onUdpSocket:self
+ didReceiveData:theCurrentReceive->buffer
+ withTag:theCurrentReceive->tag
+ fromHost:theCurrentReceive->host
+ port:theCurrentReceive->port];
+ }
+
+ if (finished)
+ {
+ if (theCurrentReceive != nil) [self endCurrentReceive]; // Caller may have disconnected.
+ }
+ return finished;
+}
+
+- (void)failCurrentReceive:(NSError *)error
+{
+ NSAssert (theCurrentReceive, @"Trying to fail current receive when there is no current receive.");
+
+ if ([theDelegate respondsToSelector:@selector(onUdpSocket:didNotReceiveDataWithTag:dueToError:)])
+ {
+ [theDelegate onUdpSocket:self didNotReceiveDataWithTag:theCurrentReceive->tag dueToError:error];
+ }
+
+ if (theCurrentReceive != nil) [self endCurrentReceive]; // Caller may have disconnected.
+}
+
+- (void)endCurrentReceive
+{
+ NSAssert (theCurrentReceive, @"Trying to end current receive when there is no current receive.");
+
+ [theReceiveTimer invalidate];
+ theReceiveTimer = nil;
+
+ [theCurrentReceive release];
+ theCurrentReceive = nil;
+}
+
+- (void)doReceiveTimeout:(NSTimer *)timer
+{
+ if (timer != theReceiveTimer) return; // Old timer. Ignore it.
+ if (theCurrentReceive != nil)
+ {
+ [self failCurrentReceive:[self getReceiveTimeoutError]];
+ [self scheduleDequeueReceive];
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#pragma mark CF Callbacks
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+- (void)doCFSocketCallback:(CFSocketCallBackType)type
+ forSocket:(CFSocketRef)sock
+ withAddress:(NSData *)address
+ withData:(const void *)pData
+{
+ NSParameterAssert((sock == theSocket4) || (sock == theSocket6));
+
+ switch (type)
+ {
+ case kCFSocketReadCallBack:
+ if(sock == theSocket4)
+ theFlags |= kSock4HasBytesAvailable;
+ else
+ theFlags |= kSock6HasBytesAvailable;
+ [self doReceive:sock];
+ break;
+ case kCFSocketWriteCallBack:
+ if(sock == theSocket4)
+ theFlags |= kSock4CanAcceptBytes;
+ else
+ theFlags |= kSock6CanAcceptBytes;
+ [self doSend:sock];
+ break;
+ default:
+ NSLog (@"AsyncUdpSocket %p received unexpected CFSocketCallBackType %d.", self, type);
+ break;
+ }
+}
+
+/**
+ * This is the callback we setup for CFSocket.
+ * This method does nothing but forward the call to it's Objective-C counterpart
+**/
+static void MyCFSocketCallback(CFSocketRef sref, CFSocketCallBackType type, CFDataRef address, const void *pData, void *pInfo)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ AsyncUdpSocket *theSocket = [[(AsyncUdpSocket *)pInfo retain] autorelease];
+ [theSocket doCFSocketCallback:type forSocket:sref withAddress:(NSData *)address withData:pData];
+
+ [pool release];
+}
+
+@end
--- /dev/null
+#import <Cocoa/Cocoa.h>
+@class AsyncSocket;
+
+@interface AppController : NSObject
+{
+ AsyncSocket *asyncSocket;
+}
+
+- (IBAction)printCert:(id)sender;
+@end
--- /dev/null
+#import "AppController.h"
+#import "AsyncSocket.h"
+#import "X509Certificate.h"
+
+
+@implementation AppController
+
+- (id)init
+{
+ if(self = [super init])
+ {
+ asyncSocket = [[AsyncSocket alloc] initWithDelegate:self];
+ }
+ return self;
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
+{
+ NSLog(@"Ready");
+
+ NSError *err = nil;
+ if(![asyncSocket connectToHost:@"paypal.com" onPort:443 error:&err])
+ {
+ NSLog(@"Error: %@", err);
+ }
+}
+
+- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
+{
+ NSLog(@"onSocket:%p didConnectToHost:%@ port:%hu", sock, host, port);
+
+ // Configure SSL/TLS settings
+ NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithCapacity:3];
+
+ /* For your regular security checks, use only this setting */
+
+ [settings setObject:@"www.paypal.com"
+ forKey:(NSString *)kCFStreamSSLPeerName];
+
+ /* To connect to a test server, with a self-signed certificate, use settings similar to this */
+
+// // Allow expired certificates
+// [settings setObject:[NSNumber numberWithBool:YES]
+// forKey:(NSString *)kCFStreamSSLAllowsExpiredCertificates];
+//
+// // Allow self-signed certificates
+// [settings setObject:[NSNumber numberWithBool:YES]
+// forKey:(NSString *)kCFStreamSSLAllowsAnyRoot];
+//
+// // In fact, don't even validate the certificate chain
+// [settings setObject:[NSNumber numberWithBool:NO]
+// forKey:(NSString *)kCFStreamSSLValidatesCertificateChain];
+
+ [sock startTLS:settings];
+}
+
+- (void)onSocket:(AsyncSocket *)sock didSecure:(BOOL)flag
+{
+ if(flag)
+ NSLog(@"onSocket:%p didSecure:YES", sock);
+ else
+ NSLog(@"onSocket:%p didSecure:NO", sock);
+}
+
+- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
+{
+ NSLog(@"onSocket:%p willDisconnectWithError:%@", sock, err);
+}
+
+- (void)onSocketDidDisconnect:(AsyncSocket *)sock
+{
+ NSLog(@"onSocketDidDisconnect:%p", sock);
+}
+
+- (IBAction)printCert:(id)sender
+{
+ NSDictionary *cert = [X509Certificate extractCertDictFromAsyncSocket:asyncSocket];
+ NSLog(@"X509 Certificate: \n%@", cert);
+}
+
+@end
--- /dev/null
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 45;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; };
+ 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
+ 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
+ 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
+ DC51C7050F2E3949007C2DA8 /* AsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = DC51C7040F2E3949007C2DA8 /* AsyncSocket.m */; };
+ DC51C70B0F2E3986007C2DA8 /* AppController.m in Sources */ = {isa = PBXBuildFile; fileRef = DC51C70A0F2E3986007C2DA8 /* AppController.m */; };
+ DC51C72F0F2E40EA007C2DA8 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC51C72E0F2E40EA007C2DA8 /* Security.framework */; };
+ DC51C8F30F2F00C9007C2DA8 /* X509Certificate.m in Sources */ = {isa = PBXBuildFile; fileRef = DC51C8F20F2F00C9007C2DA8 /* X509Certificate.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
+ 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
+ 1DDD58150DA1D0A300B32029 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = "<group>"; };
+ 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
+ 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+ 32CA4F630368D1EE00C91783 /* CertTest_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CertTest_Prefix.pch; sourceTree = "<group>"; };
+ 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 8D1107320486CEB800E47090 /* CertTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CertTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ DC51C7030F2E3949007C2DA8 /* AsyncSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AsyncSocket.h; path = ../AsyncSocket.h; sourceTree = SOURCE_ROOT; };
+ DC51C7040F2E3949007C2DA8 /* AsyncSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AsyncSocket.m; path = ../AsyncSocket.m; sourceTree = SOURCE_ROOT; };
+ DC51C7090F2E3986007C2DA8 /* AppController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppController.h; sourceTree = "<group>"; };
+ DC51C70A0F2E3986007C2DA8 /* AppController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppController.m; sourceTree = "<group>"; };
+ DC51C72E0F2E40EA007C2DA8 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = "<absolute>"; };
+ DC51C8F10F2F00C9007C2DA8 /* X509Certificate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = X509Certificate.h; sourceTree = "<group>"; };
+ DC51C8F20F2F00C9007C2DA8 /* X509Certificate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = X509Certificate.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8D11072E0486CEB800E47090 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
+ DC51C72F0F2E40EA007C2DA8 /* Security.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 080E96DDFE201D6D7F000001 /* Classes */ = {
+ isa = PBXGroup;
+ children = (
+ DC51C7030F2E3949007C2DA8 /* AsyncSocket.h */,
+ DC51C7040F2E3949007C2DA8 /* AsyncSocket.m */,
+ DC51C8F10F2F00C9007C2DA8 /* X509Certificate.h */,
+ DC51C8F20F2F00C9007C2DA8 /* X509Certificate.m */,
+ DC51C7090F2E3986007C2DA8 /* AppController.h */,
+ DC51C70A0F2E3986007C2DA8 /* AppController.m */,
+ );
+ name = Classes;
+ sourceTree = "<group>";
+ };
+ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
+ DC51C72E0F2E40EA007C2DA8 /* Security.framework */,
+ );
+ name = "Linked Frameworks";
+ sourceTree = "<group>";
+ };
+ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 29B97324FDCFA39411CA2CEA /* AppKit.framework */,
+ 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
+ 29B97325FDCFA39411CA2CEA /* Foundation.framework */,
+ );
+ name = "Other Frameworks";
+ sourceTree = "<group>";
+ };
+ 19C28FACFE9D520D11CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8D1107320486CEB800E47090 /* CertTest.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 29B97314FDCFA39411CA2CEA /* CertTest */ = {
+ isa = PBXGroup;
+ children = (
+ 080E96DDFE201D6D7F000001 /* Classes */,
+ 29B97315FDCFA39411CA2CEA /* Other Sources */,
+ 29B97317FDCFA39411CA2CEA /* Resources */,
+ 29B97323FDCFA39411CA2CEA /* Frameworks */,
+ 19C28FACFE9D520D11CA2CBB /* Products */,
+ );
+ name = CertTest;
+ sourceTree = "<group>";
+ };
+ 29B97315FDCFA39411CA2CEA /* Other Sources */ = {
+ isa = PBXGroup;
+ children = (
+ 32CA4F630368D1EE00C91783 /* CertTest_Prefix.pch */,
+ 29B97316FDCFA39411CA2CEA /* main.m */,
+ );
+ name = "Other Sources";
+ sourceTree = "<group>";
+ };
+ 29B97317FDCFA39411CA2CEA /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 8D1107310486CEB800E47090 /* Info.plist */,
+ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
+ 1DDD58140DA1D0A300B32029 /* MainMenu.xib */,
+ );
+ name = Resources;
+ sourceTree = "<group>";
+ };
+ 29B97323FDCFA39411CA2CEA /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
+ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 8D1107260486CEB800E47090 /* CertTest */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "CertTest" */;
+ buildPhases = (
+ 8D1107290486CEB800E47090 /* Resources */,
+ 8D11072C0486CEB800E47090 /* Sources */,
+ 8D11072E0486CEB800E47090 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = CertTest;
+ productInstallPath = "$(HOME)/Applications";
+ productName = CertTest;
+ productReference = 8D1107320486CEB800E47090 /* CertTest.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 29B97313FDCFA39411CA2CEA /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "CertTest" */;
+ compatibilityVersion = "Xcode 3.1";
+ hasScannedForEncodings = 1;
+ mainGroup = 29B97314FDCFA39411CA2CEA /* CertTest */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 8D1107260486CEB800E47090 /* CertTest */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 8D1107290486CEB800E47090 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
+ 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8D11072C0486CEB800E47090 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D11072D0486CEB800E47090 /* main.m in Sources */,
+ DC51C7050F2E3949007C2DA8 /* AsyncSocket.m in Sources */,
+ DC51C70B0F2E3986007C2DA8 /* AppController.m in Sources */,
+ DC51C8F30F2F00C9007C2DA8 /* X509Certificate.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 089C165DFE840E0CC02AAC07 /* English */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ 1DDD58140DA1D0A300B32029 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 1DDD58150DA1D0A300B32029 /* English */,
+ );
+ name = MainMenu.xib;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ C01FCF4B08A954540054247B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = CertTest_Prefix.pch;
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ PRODUCT_NAME = CertTest;
+ };
+ name = Debug;
+ };
+ C01FCF4C08A954540054247B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = CertTest_Prefix.pch;
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ PRODUCT_NAME = CertTest;
+ };
+ name = Release;
+ };
+ C01FCF4F08A954540054247B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ PREBINDING = NO;
+ SDKROOT = macosx10.5;
+ };
+ name = Debug;
+ };
+ C01FCF5008A954540054247B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ PREBINDING = NO;
+ SDKROOT = macosx10.5;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "CertTest" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C01FCF4B08A954540054247B /* Debug */,
+ C01FCF4C08A954540054247B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ C01FCF4E08A954540054247B /* Build configuration list for PBXProject "CertTest" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C01FCF4F08A954540054247B /* Debug */,
+ C01FCF5008A954540054247B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
--- /dev/null
+//
+// Prefix header for all source files of the 'CertTest' target in the 'CertTest' project
+//
+
+#ifdef __OBJC__
+ #import <Cocoa/Cocoa.h>
+#endif
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
+ <data>
+ <int key="IBDocument.SystemTarget">1050</int>
+ <string key="IBDocument.SystemVersion">9G55</string>
+ <string key="IBDocument.InterfaceBuilderVersion">677</string>
+ <string key="IBDocument.AppKitVersion">949.43</string>
+ <string key="IBDocument.HIToolboxVersion">353.00</string>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="372"/>
+ <integer value="29"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1021">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSCustomObject" id="1014">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1050">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSMenu" id="649796088">
+ <string key="NSTitle">AMainMenu</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="694149608">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">NewApplication</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <object class="NSCustomResource" key="NSOnImage" id="35465992">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuCheckmark</string>
+ </object>
+ <object class="NSCustomResource" key="NSMixedImage" id="502551668">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuMixedState</string>
+ </object>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="110575045">
+ <string key="NSTitle">NewApplication</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="238522557">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">About NewApplication</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="304266470">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="609285721">
+ <reference key="NSMenu" ref="110575045"/>
+ <string type="base64-UTF8" key="NSTitle">UHJlZmVyZW5jZXPigKY</string>
+ <string key="NSKeyEquiv">,</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="481834944">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1046388886">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Services</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="752062318">
+ <string key="NSTitle">Services</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <string key="NSName">_NSServicesMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="646227648">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="755159360">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Hide NewApplication</string>
+ <string key="NSKeyEquiv">h</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="342932134">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Hide Others</string>
+ <string key="NSKeyEquiv">h</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="908899353">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Show All</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1056857174">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="632727374">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Quit NewApplication</string>
+ <string key="NSKeyEquiv">q</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ <string key="NSName">_NSAppleMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="379814623">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">File</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="720053764">
+ <string key="NSTitle">File</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="705341025">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">New</string>
+ <string key="NSKeyEquiv">n</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="722745758">
+ <reference key="NSMenu" ref="720053764"/>
+ <string type="base64-UTF8" key="NSTitle">T3BlbuKApg</string>
+ <string key="NSKeyEquiv">o</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1025936716">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Open Recent</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="1065607017">
+ <string key="NSTitle">Open Recent</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="759406840">
+ <reference key="NSMenu" ref="1065607017"/>
+ <string key="NSTitle">Clear Menu</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ <string key="NSName">_NSRecentDocumentsMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="425164168">
+ <reference key="NSMenu" ref="720053764"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="776162233">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Close</string>
+ <string key="NSKeyEquiv">w</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1023925487">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Save</string>
+ <string key="NSKeyEquiv">s</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="117038363">
+ <reference key="NSMenu" ref="720053764"/>
+ <string type="base64-UTF8" key="NSTitle">U2F2ZSBBc+KApg</string>
+ <string key="NSKeyEquiv">S</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="579971712">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Revert to Saved</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1010469920">
+ <reference key="NSMenu" ref="720053764"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="294629803">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Page Setup...</string>
+ <string key="NSKeyEquiv">P</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSToolTip"/>
+ </object>
+ <object class="NSMenuItem" id="49223823">
+ <reference key="NSMenu" ref="720053764"/>
+ <string type="base64-UTF8" key="NSTitle">UHJpbnTigKY</string>
+ <string key="NSKeyEquiv">p</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="952259628">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Edit</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="789758025">
+ <string key="NSTitle">Edit</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="1058277027">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Undo</string>
+ <string key="NSKeyEquiv">z</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="790794224">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Redo</string>
+ <string key="NSKeyEquiv">Z</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1040322652">
+ <reference key="NSMenu" ref="789758025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="296257095">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Cut</string>
+ <string key="NSKeyEquiv">x</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="860595796">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Copy</string>
+ <string key="NSKeyEquiv">c</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="29853731">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Paste</string>
+ <string key="NSKeyEquiv">v</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="437104165">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Delete</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="583158037">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Select All</string>
+ <string key="NSKeyEquiv">a</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="212016141">
+ <reference key="NSMenu" ref="789758025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="892235320">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Find</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="963351320">
+ <string key="NSTitle">Find</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="447796847">
+ <reference key="NSMenu" ref="963351320"/>
+ <string type="base64-UTF8" key="NSTitle">RmluZOKApg</string>
+ <string key="NSKeyEquiv">f</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="326711663">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Find Next</string>
+ <string key="NSKeyEquiv">g</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="270902937">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Find Previous</string>
+ <string key="NSKeyEquiv">G</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">3</int>
+ </object>
+ <object class="NSMenuItem" id="159080638">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Use Selection for Find</string>
+ <string key="NSKeyEquiv">e</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">7</int>
+ </object>
+ <object class="NSMenuItem" id="88285865">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Jump to Selection</string>
+ <string key="NSKeyEquiv">j</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="972420730">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Spelling and Grammar</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="769623530">
+ <string key="NSTitle">Spelling and Grammar</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="679648819">
+ <reference key="NSMenu" ref="769623530"/>
+ <string type="base64-UTF8" key="NSTitle">U2hvdyBTcGVsbGluZ+KApg</string>
+ <string key="NSKeyEquiv">:</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="96193923">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Spelling</string>
+ <string key="NSKeyEquiv">;</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="948374510">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Spelling While Typing</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="967646866">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Grammar With Spelling</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="507821607">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Substitutions</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="698887838">
+ <string key="NSTitle">Substitutions</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="605118523">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Copy/Paste</string>
+ <string key="NSKeyEquiv">f</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="197661976">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Quotes</string>
+ <string key="NSKeyEquiv">g</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="708854459">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Links</string>
+ <string key="NSKeyEquiv">G</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">3</int>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="676164635">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Speech</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="785027613">
+ <string key="NSTitle">Speech</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="731782645">
+ <reference key="NSMenu" ref="785027613"/>
+ <string key="NSTitle">Start Speaking</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="680220178">
+ <reference key="NSMenu" ref="785027613"/>
+ <string key="NSTitle">Stop Speaking</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="302598603">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Format</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="941447902">
+ <string key="NSTitle">Format</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="792887677">
+ <reference key="NSMenu" ref="941447902"/>
+ <string key="NSTitle">Font</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="786677654">
+ <string key="NSTitle">Font</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="159677712">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Show Fonts</string>
+ <string key="NSKeyEquiv">t</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="305399458">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Bold</string>
+ <string key="NSKeyEquiv">b</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="814362025">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Italic</string>
+ <string key="NSKeyEquiv">i</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="330926929">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Underline</string>
+ <string key="NSKeyEquiv">u</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="533507878">
+ <reference key="NSMenu" ref="786677654"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="158063935">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Bigger</string>
+ <string key="NSKeyEquiv">+</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">3</int>
+ </object>
+ <object class="NSMenuItem" id="885547335">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Smaller</string>
+ <string key="NSKeyEquiv">-</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">4</int>
+ </object>
+ <object class="NSMenuItem" id="901062459">
+ <reference key="NSMenu" ref="786677654"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="767671776">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Kern</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="175441468">
+ <string key="NSTitle">Kern</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="252969304">
+ <reference key="NSMenu" ref="175441468"/>
+ <string key="NSTitle">Use Default</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="766922938">
+ <reference key="NSMenu" ref="175441468"/>
+ <string key="NSTitle">Use None</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="677519740">
+ <reference key="NSMenu" ref="175441468"/>
+ <string key="NSTitle">Tighten</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="238351151">
+ <reference key="NSMenu" ref="175441468"/>
+ <string key="NSTitle">Loosen</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="691570813">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Ligature</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="1058217995">
+ <string key="NSTitle">Ligature</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="706297211">
+ <reference key="NSMenu" ref="1058217995"/>
+ <string key="NSTitle">Use Default</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="568384683">
+ <reference key="NSMenu" ref="1058217995"/>
+ <string key="NSTitle">Use None</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="663508465">
+ <reference key="NSMenu" ref="1058217995"/>
+ <string key="NSTitle">Use All</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="769124883">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Baseline</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="18263474">
+ <string key="NSTitle">Baseline</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="257962622">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Use Default</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="644725453">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Superscript</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1037576581">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Subscript</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="941806246">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Raise</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1045724900">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Lower</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="739652853">
+ <reference key="NSMenu" ref="786677654"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1012600125">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Show Colors</string>
+ <string key="NSKeyEquiv">C</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="214559597">
+ <reference key="NSMenu" ref="786677654"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="596732606">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Copy Style</string>
+ <string key="NSKeyEquiv">c</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="393423671">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Paste Style</string>
+ <string key="NSKeyEquiv">v</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ <string key="NSName">_NSFontMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="15516124">
+ <reference key="NSMenu" ref="941447902"/>
+ <string key="NSTitle">Text</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="23081656">
+ <string key="NSTitle">Text</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="135107054">
+ <reference key="NSMenu" ref="23081656"/>
+ <string key="NSTitle">Align Left</string>
+ <string key="NSKeyEquiv">{</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="310547522">
+ <reference key="NSMenu" ref="23081656"/>
+ <string key="NSTitle">Center</string>
+ <string key="NSKeyEquiv">|</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="436088763">
+ <reference key="NSMenu" ref="23081656"/>
+ <string key="NSTitle">Justify</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="498119243">
+ <reference key="NSMenu" ref="23081656"/>
+ <string key="NSTitle">Align Right</string>
+ <string key="NSKeyEquiv">}</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="607995063">
+ <reference key="NSMenu" ref="23081656"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="420564933">
+ <reference key="NSMenu" ref="23081656"/>
+ <string key="NSTitle">Show Ruler</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="479856769">
+ <reference key="NSMenu" ref="23081656"/>
+ <string key="NSTitle">Copy Ruler</string>
+ <string key="NSKeyEquiv">c</string>
+ <int key="NSKeyEquivModMask">1310720</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="333628178">
+ <reference key="NSMenu" ref="23081656"/>
+ <string key="NSTitle">Paste Ruler</string>
+ <string key="NSKeyEquiv">v</string>
+ <int key="NSKeyEquivModMask">1310720</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="586577488">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">View</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="466310130">
+ <string key="NSTitle">View</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="102151532">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Show Toolbar</string>
+ <string key="NSKeyEquiv">t</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="237841660">
+ <reference key="NSMenu" ref="466310130"/>
+ <string type="base64-UTF8" key="NSTitle">Q3VzdG9taXplIFRvb2xiYXLigKY</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="713487014">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Window</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="835318025">
+ <string key="NSTitle">Window</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="1011231497">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Minimize</string>
+ <string key="NSKeyEquiv">m</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="575023229">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Zoom</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="299356726">
+ <reference key="NSMenu" ref="835318025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="625202149">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Bring All to Front</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ <string key="NSName">_NSWindowsMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="391199113">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Help</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="374024848">
+ <string key="NSTitle">Help</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="238773614">
+ <reference key="NSMenu" ref="374024848"/>
+ <string key="NSTitle">NewApplication Help</string>
+ <string key="NSKeyEquiv">?</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <string key="NSName">_NSMainMenu</string>
+ </object>
+ <object class="NSWindowTemplate" id="972006081">
+ <int key="NSWindowStyleMask">15</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{335, 390}, {480, 360}}</string>
+ <int key="NSWTFlags">1946157056</int>
+ <string key="NSWindowTitle">Window</string>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ <object class="NSView" key="NSWindowView" id="439893737">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSButton" id="366178669">
+ <reference key="NSNextResponder" ref="439893737"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{181, 312}, {100, 32}}</string>
+ <reference key="NSSuperview" ref="439893737"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="326037345">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Print Cert</string>
+ <object class="NSFont" key="NSSupport">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.300000e+01</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="366178669"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ </object>
+ <string key="NSFrameSize">{480, 360}</string>
+ <reference key="NSSuperview"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
+ <string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ </object>
+ <object class="NSCustomObject" id="755631768">
+ <string key="NSClassName">NSFontManager</string>
+ </object>
+ <object class="NSCustomObject" id="304411430">
+ <string key="NSClassName">AppController</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performMiniaturize:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1011231497"/>
+ </object>
+ <int key="connectionID">37</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">arrangeInFront:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="625202149"/>
+ </object>
+ <int key="connectionID">39</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">print:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="49223823"/>
+ </object>
+ <int key="connectionID">86</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">runPageLayout:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="294629803"/>
+ </object>
+ <int key="connectionID">87</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">clearRecentDocuments:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="759406840"/>
+ </object>
+ <int key="connectionID">127</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontStandardAboutPanel:</string>
+ <reference key="source" ref="1021"/>
+ <reference key="destination" ref="238522557"/>
+ </object>
+ <int key="connectionID">142</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performClose:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="776162233"/>
+ </object>
+ <int key="connectionID">193</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleContinuousSpellChecking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="948374510"/>
+ </object>
+ <int key="connectionID">222</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">undo:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1058277027"/>
+ </object>
+ <int key="connectionID">223</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">copy:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="860595796"/>
+ </object>
+ <int key="connectionID">224</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">checkSpelling:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="96193923"/>
+ </object>
+ <int key="connectionID">225</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">paste:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="29853731"/>
+ </object>
+ <int key="connectionID">226</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">stopSpeaking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="680220178"/>
+ </object>
+ <int key="connectionID">227</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">cut:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="296257095"/>
+ </object>
+ <int key="connectionID">228</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showGuessPanel:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="679648819"/>
+ </object>
+ <int key="connectionID">230</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">redo:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="790794224"/>
+ </object>
+ <int key="connectionID">231</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">selectAll:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="583158037"/>
+ </object>
+ <int key="connectionID">232</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">startSpeaking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="731782645"/>
+ </object>
+ <int key="connectionID">233</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">delete:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="437104165"/>
+ </object>
+ <int key="connectionID">235</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performZoom:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="575023229"/>
+ </object>
+ <int key="connectionID">240</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performFindPanelAction:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="447796847"/>
+ </object>
+ <int key="connectionID">241</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">centerSelectionInVisibleArea:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="88285865"/>
+ </object>
+ <int key="connectionID">245</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleGrammarChecking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="967646866"/>
+ </object>
+ <int key="connectionID">347</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleSmartInsertDelete:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="605118523"/>
+ </object>
+ <int key="connectionID">355</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticQuoteSubstitution:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="197661976"/>
+ </object>
+ <int key="connectionID">356</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticLinkDetection:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="708854459"/>
+ </object>
+ <int key="connectionID">357</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showHelp:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="238773614"/>
+ </object>
+ <int key="connectionID">360</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">saveDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1023925487"/>
+ </object>
+ <int key="connectionID">362</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">saveDocumentAs:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="117038363"/>
+ </object>
+ <int key="connectionID">363</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">revertDocumentToSaved:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="579971712"/>
+ </object>
+ <int key="connectionID">364</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">runToolbarCustomizationPalette:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="237841660"/>
+ </object>
+ <int key="connectionID">365</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleToolbarShown:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="102151532"/>
+ </object>
+ <int key="connectionID">366</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">hide:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="755159360"/>
+ </object>
+ <int key="connectionID">367</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">hideOtherApplications:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="342932134"/>
+ </object>
+ <int key="connectionID">368</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">unhideAllApplications:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="908899353"/>
+ </object>
+ <int key="connectionID">370</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">newDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="705341025"/>
+ </object>
+ <int key="connectionID">373</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">openDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="722745758"/>
+ </object>
+ <int key="connectionID">374</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">addFontTrait:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="305399458"/>
+ </object>
+ <int key="connectionID">421</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">addFontTrait:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="814362025"/>
+ </object>
+ <int key="connectionID">422</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">modifyFont:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="885547335"/>
+ </object>
+ <int key="connectionID">423</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontFontPanel:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="159677712"/>
+ </object>
+ <int key="connectionID">424</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">modifyFont:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="158063935"/>
+ </object>
+ <int key="connectionID">425</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">raiseBaseline:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="941806246"/>
+ </object>
+ <int key="connectionID">426</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">lowerBaseline:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1045724900"/>
+ </object>
+ <int key="connectionID">427</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">copyFont:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="596732606"/>
+ </object>
+ <int key="connectionID">428</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">subscript:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1037576581"/>
+ </object>
+ <int key="connectionID">429</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">superscript:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="644725453"/>
+ </object>
+ <int key="connectionID">430</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">tightenKerning:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="677519740"/>
+ </object>
+ <int key="connectionID">431</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">underline:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="330926929"/>
+ </object>
+ <int key="connectionID">432</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontColorPanel:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1012600125"/>
+ </object>
+ <int key="connectionID">433</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">useAllLigatures:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="663508465"/>
+ </object>
+ <int key="connectionID">434</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">loosenKerning:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="238351151"/>
+ </object>
+ <int key="connectionID">435</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">pasteFont:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="393423671"/>
+ </object>
+ <int key="connectionID">436</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">unscript:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="257962622"/>
+ </object>
+ <int key="connectionID">437</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">useStandardKerning:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="252969304"/>
+ </object>
+ <int key="connectionID">438</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">useStandardLigatures:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="706297211"/>
+ </object>
+ <int key="connectionID">439</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">turnOffLigatures:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="568384683"/>
+ </object>
+ <int key="connectionID">440</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">turnOffKerning:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="766922938"/>
+ </object>
+ <int key="connectionID">441</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">alignLeft:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="135107054"/>
+ </object>
+ <int key="connectionID">442</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">alignJustified:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="436088763"/>
+ </object>
+ <int key="connectionID">443</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">copyRuler:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="479856769"/>
+ </object>
+ <int key="connectionID">444</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">alignCenter:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="310547522"/>
+ </object>
+ <int key="connectionID">445</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleRuler:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="420564933"/>
+ </object>
+ <int key="connectionID">446</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">alignRight:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="498119243"/>
+ </object>
+ <int key="connectionID">447</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">pasteRuler:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="333628178"/>
+ </object>
+ <int key="connectionID">448</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">terminate:</string>
+ <reference key="source" ref="1050"/>
+ <reference key="destination" ref="632727374"/>
+ </object>
+ <int key="connectionID">449</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="1050"/>
+ <reference key="destination" ref="304411430"/>
+ </object>
+ <int key="connectionID">451</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">printCert:</string>
+ <reference key="source" ref="304411430"/>
+ <reference key="destination" ref="366178669"/>
+ </object>
+ <int key="connectionID">454</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="1049">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1048"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1021"/>
+ <reference key="parent" ref="1049"/>
+ <string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1014"/>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1050"/>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">29</int>
+ <reference key="object" ref="649796088"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="713487014"/>
+ <reference ref="694149608"/>
+ <reference ref="391199113"/>
+ <reference ref="952259628"/>
+ <reference ref="379814623"/>
+ <reference ref="586577488"/>
+ <reference ref="302598603"/>
+ </object>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">MainMenu</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">19</int>
+ <reference key="object" ref="713487014"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="835318025"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">56</int>
+ <reference key="object" ref="694149608"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="110575045"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">103</int>
+ <reference key="object" ref="391199113"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="374024848"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ <string key="objectName">1</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">217</int>
+ <reference key="object" ref="952259628"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="789758025"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">83</int>
+ <reference key="object" ref="379814623"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="720053764"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">81</int>
+ <reference key="object" ref="720053764"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1023925487"/>
+ <reference ref="117038363"/>
+ <reference ref="49223823"/>
+ <reference ref="722745758"/>
+ <reference ref="705341025"/>
+ <reference ref="1025936716"/>
+ <reference ref="294629803"/>
+ <reference ref="776162233"/>
+ <reference ref="425164168"/>
+ <reference ref="579971712"/>
+ <reference ref="1010469920"/>
+ </object>
+ <reference key="parent" ref="379814623"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">75</int>
+ <reference key="object" ref="1023925487"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">3</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">80</int>
+ <reference key="object" ref="117038363"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">8</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">78</int>
+ <reference key="object" ref="49223823"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">6</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">72</int>
+ <reference key="object" ref="722745758"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">82</int>
+ <reference key="object" ref="705341025"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">9</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">124</int>
+ <reference key="object" ref="1025936716"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1065607017"/>
+ </object>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">77</int>
+ <reference key="object" ref="294629803"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">5</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">73</int>
+ <reference key="object" ref="776162233"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">1</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">79</int>
+ <reference key="object" ref="425164168"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">7</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">112</int>
+ <reference key="object" ref="579971712"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">10</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">74</int>
+ <reference key="object" ref="1010469920"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">2</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">125</int>
+ <reference key="object" ref="1065607017"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="759406840"/>
+ </object>
+ <reference key="parent" ref="1025936716"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">126</int>
+ <reference key="object" ref="759406840"/>
+ <reference key="parent" ref="1065607017"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">205</int>
+ <reference key="object" ref="789758025"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="437104165"/>
+ <reference ref="583158037"/>
+ <reference ref="1058277027"/>
+ <reference ref="212016141"/>
+ <reference ref="296257095"/>
+ <reference ref="29853731"/>
+ <reference ref="860595796"/>
+ <reference ref="1040322652"/>
+ <reference ref="790794224"/>
+ <reference ref="892235320"/>
+ <reference ref="972420730"/>
+ <reference ref="676164635"/>
+ <reference ref="507821607"/>
+ </object>
+ <reference key="parent" ref="952259628"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">202</int>
+ <reference key="object" ref="437104165"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">198</int>
+ <reference key="object" ref="583158037"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">207</int>
+ <reference key="object" ref="1058277027"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">214</int>
+ <reference key="object" ref="212016141"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">199</int>
+ <reference key="object" ref="296257095"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">203</int>
+ <reference key="object" ref="29853731"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">197</int>
+ <reference key="object" ref="860595796"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">206</int>
+ <reference key="object" ref="1040322652"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">215</int>
+ <reference key="object" ref="790794224"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">218</int>
+ <reference key="object" ref="892235320"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="963351320"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">216</int>
+ <reference key="object" ref="972420730"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="769623530"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">200</int>
+ <reference key="object" ref="769623530"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="948374510"/>
+ <reference ref="96193923"/>
+ <reference ref="679648819"/>
+ <reference ref="967646866"/>
+ </object>
+ <reference key="parent" ref="972420730"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">219</int>
+ <reference key="object" ref="948374510"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">201</int>
+ <reference key="object" ref="96193923"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">204</int>
+ <reference key="object" ref="679648819"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">220</int>
+ <reference key="object" ref="963351320"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="270902937"/>
+ <reference ref="88285865"/>
+ <reference ref="159080638"/>
+ <reference ref="326711663"/>
+ <reference ref="447796847"/>
+ </object>
+ <reference key="parent" ref="892235320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">213</int>
+ <reference key="object" ref="270902937"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">210</int>
+ <reference key="object" ref="88285865"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">221</int>
+ <reference key="object" ref="159080638"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">208</int>
+ <reference key="object" ref="326711663"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">209</int>
+ <reference key="object" ref="447796847"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">106</int>
+ <reference key="object" ref="374024848"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="238773614"/>
+ </object>
+ <reference key="parent" ref="391199113"/>
+ <string key="objectName">2</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">111</int>
+ <reference key="object" ref="238773614"/>
+ <reference key="parent" ref="374024848"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">57</int>
+ <reference key="object" ref="110575045"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="238522557"/>
+ <reference ref="755159360"/>
+ <reference ref="908899353"/>
+ <reference ref="632727374"/>
+ <reference ref="646227648"/>
+ <reference ref="609285721"/>
+ <reference ref="481834944"/>
+ <reference ref="304266470"/>
+ <reference ref="1046388886"/>
+ <reference ref="1056857174"/>
+ <reference ref="342932134"/>
+ </object>
+ <reference key="parent" ref="694149608"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">58</int>
+ <reference key="object" ref="238522557"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">134</int>
+ <reference key="object" ref="755159360"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">150</int>
+ <reference key="object" ref="908899353"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">136</int>
+ <reference key="object" ref="632727374"/>
+ <reference key="parent" ref="110575045"/>
+ <string key="objectName">1111</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">144</int>
+ <reference key="object" ref="646227648"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">129</int>
+ <reference key="object" ref="609285721"/>
+ <reference key="parent" ref="110575045"/>
+ <string key="objectName">121</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">143</int>
+ <reference key="object" ref="481834944"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">236</int>
+ <reference key="object" ref="304266470"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">131</int>
+ <reference key="object" ref="1046388886"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="752062318"/>
+ </object>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">149</int>
+ <reference key="object" ref="1056857174"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">145</int>
+ <reference key="object" ref="342932134"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">130</int>
+ <reference key="object" ref="752062318"/>
+ <reference key="parent" ref="1046388886"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">24</int>
+ <reference key="object" ref="835318025"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="299356726"/>
+ <reference ref="625202149"/>
+ <reference ref="575023229"/>
+ <reference ref="1011231497"/>
+ </object>
+ <reference key="parent" ref="713487014"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">92</int>
+ <reference key="object" ref="299356726"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5</int>
+ <reference key="object" ref="625202149"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">239</int>
+ <reference key="object" ref="575023229"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">23</int>
+ <reference key="object" ref="1011231497"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">295</int>
+ <reference key="object" ref="586577488"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="466310130"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">296</int>
+ <reference key="object" ref="466310130"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="102151532"/>
+ <reference ref="237841660"/>
+ </object>
+ <reference key="parent" ref="586577488"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">297</int>
+ <reference key="object" ref="102151532"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">298</int>
+ <reference key="object" ref="237841660"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">211</int>
+ <reference key="object" ref="676164635"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="785027613"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">212</int>
+ <reference key="object" ref="785027613"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="680220178"/>
+ <reference ref="731782645"/>
+ </object>
+ <reference key="parent" ref="676164635"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">195</int>
+ <reference key="object" ref="680220178"/>
+ <reference key="parent" ref="785027613"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">196</int>
+ <reference key="object" ref="731782645"/>
+ <reference key="parent" ref="785027613"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">346</int>
+ <reference key="object" ref="967646866"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">348</int>
+ <reference key="object" ref="507821607"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="698887838"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">349</int>
+ <reference key="object" ref="698887838"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="605118523"/>
+ <reference ref="197661976"/>
+ <reference ref="708854459"/>
+ </object>
+ <reference key="parent" ref="507821607"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">350</int>
+ <reference key="object" ref="605118523"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">351</int>
+ <reference key="object" ref="197661976"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">354</int>
+ <reference key="object" ref="708854459"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">371</int>
+ <reference key="object" ref="972006081"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="439893737"/>
+ </object>
+ <reference key="parent" ref="1049"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">372</int>
+ <reference key="object" ref="439893737"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="366178669"/>
+ </object>
+ <reference key="parent" ref="972006081"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">375</int>
+ <reference key="object" ref="302598603"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="941447902"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">376</int>
+ <reference key="object" ref="941447902"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="792887677"/>
+ <reference ref="15516124"/>
+ </object>
+ <reference key="parent" ref="302598603"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">377</int>
+ <reference key="object" ref="792887677"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="786677654"/>
+ </object>
+ <reference key="parent" ref="941447902"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">378</int>
+ <reference key="object" ref="15516124"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="23081656"/>
+ </object>
+ <reference key="parent" ref="941447902"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">379</int>
+ <reference key="object" ref="23081656"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="135107054"/>
+ <reference ref="310547522"/>
+ <reference ref="436088763"/>
+ <reference ref="498119243"/>
+ <reference ref="607995063"/>
+ <reference ref="420564933"/>
+ <reference ref="479856769"/>
+ <reference ref="333628178"/>
+ </object>
+ <reference key="parent" ref="15516124"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">380</int>
+ <reference key="object" ref="135107054"/>
+ <reference key="parent" ref="23081656"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">381</int>
+ <reference key="object" ref="310547522"/>
+ <reference key="parent" ref="23081656"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">382</int>
+ <reference key="object" ref="436088763"/>
+ <reference key="parent" ref="23081656"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">383</int>
+ <reference key="object" ref="498119243"/>
+ <reference key="parent" ref="23081656"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">384</int>
+ <reference key="object" ref="607995063"/>
+ <reference key="parent" ref="23081656"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">385</int>
+ <reference key="object" ref="420564933"/>
+ <reference key="parent" ref="23081656"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">386</int>
+ <reference key="object" ref="479856769"/>
+ <reference key="parent" ref="23081656"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">387</int>
+ <reference key="object" ref="333628178"/>
+ <reference key="parent" ref="23081656"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">388</int>
+ <reference key="object" ref="786677654"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="159677712"/>
+ <reference ref="305399458"/>
+ <reference ref="814362025"/>
+ <reference ref="330926929"/>
+ <reference ref="533507878"/>
+ <reference ref="158063935"/>
+ <reference ref="885547335"/>
+ <reference ref="901062459"/>
+ <reference ref="767671776"/>
+ <reference ref="691570813"/>
+ <reference ref="769124883"/>
+ <reference ref="739652853"/>
+ <reference ref="1012600125"/>
+ <reference ref="214559597"/>
+ <reference ref="596732606"/>
+ <reference ref="393423671"/>
+ </object>
+ <reference key="parent" ref="792887677"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">389</int>
+ <reference key="object" ref="159677712"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">390</int>
+ <reference key="object" ref="305399458"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">391</int>
+ <reference key="object" ref="814362025"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">392</int>
+ <reference key="object" ref="330926929"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">393</int>
+ <reference key="object" ref="533507878"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">394</int>
+ <reference key="object" ref="158063935"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">395</int>
+ <reference key="object" ref="885547335"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">396</int>
+ <reference key="object" ref="901062459"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">397</int>
+ <reference key="object" ref="767671776"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="175441468"/>
+ </object>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">398</int>
+ <reference key="object" ref="691570813"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1058217995"/>
+ </object>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">399</int>
+ <reference key="object" ref="769124883"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="18263474"/>
+ </object>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">400</int>
+ <reference key="object" ref="739652853"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">401</int>
+ <reference key="object" ref="1012600125"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">402</int>
+ <reference key="object" ref="214559597"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">403</int>
+ <reference key="object" ref="596732606"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">404</int>
+ <reference key="object" ref="393423671"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">405</int>
+ <reference key="object" ref="18263474"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="257962622"/>
+ <reference ref="644725453"/>
+ <reference ref="1037576581"/>
+ <reference ref="941806246"/>
+ <reference ref="1045724900"/>
+ </object>
+ <reference key="parent" ref="769124883"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">406</int>
+ <reference key="object" ref="257962622"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">407</int>
+ <reference key="object" ref="644725453"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">408</int>
+ <reference key="object" ref="1037576581"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">409</int>
+ <reference key="object" ref="941806246"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">410</int>
+ <reference key="object" ref="1045724900"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">411</int>
+ <reference key="object" ref="1058217995"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="706297211"/>
+ <reference ref="568384683"/>
+ <reference ref="663508465"/>
+ </object>
+ <reference key="parent" ref="691570813"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">412</int>
+ <reference key="object" ref="706297211"/>
+ <reference key="parent" ref="1058217995"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">413</int>
+ <reference key="object" ref="568384683"/>
+ <reference key="parent" ref="1058217995"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">414</int>
+ <reference key="object" ref="663508465"/>
+ <reference key="parent" ref="1058217995"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">415</int>
+ <reference key="object" ref="175441468"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="252969304"/>
+ <reference ref="766922938"/>
+ <reference ref="677519740"/>
+ <reference ref="238351151"/>
+ </object>
+ <reference key="parent" ref="767671776"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">416</int>
+ <reference key="object" ref="252969304"/>
+ <reference key="parent" ref="175441468"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">417</int>
+ <reference key="object" ref="766922938"/>
+ <reference key="parent" ref="175441468"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">418</int>
+ <reference key="object" ref="677519740"/>
+ <reference key="parent" ref="175441468"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">419</int>
+ <reference key="object" ref="238351151"/>
+ <reference key="parent" ref="175441468"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">420</int>
+ <reference key="object" ref="755631768"/>
+ <reference key="parent" ref="1049"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">450</int>
+ <reference key="object" ref="304411430"/>
+ <reference key="parent" ref="1049"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">452</int>
+ <reference key="object" ref="366178669"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="326037345"/>
+ </object>
+ <reference key="parent" ref="439893737"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">453</int>
+ <reference key="object" ref="326037345"/>
+ <reference key="parent" ref="366178669"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-1.IBPluginDependency</string>
+ <string>-2.IBPluginDependency</string>
+ <string>-3.IBPluginDependency</string>
+ <string>103.IBPluginDependency</string>
+ <string>103.ImportedFromIB2</string>
+ <string>106.IBPluginDependency</string>
+ <string>106.ImportedFromIB2</string>
+ <string>106.editorWindowContentRectSynchronizationRect</string>
+ <string>111.IBPluginDependency</string>
+ <string>111.ImportedFromIB2</string>
+ <string>112.IBPluginDependency</string>
+ <string>112.ImportedFromIB2</string>
+ <string>124.IBPluginDependency</string>
+ <string>124.ImportedFromIB2</string>
+ <string>125.IBPluginDependency</string>
+ <string>125.ImportedFromIB2</string>
+ <string>125.editorWindowContentRectSynchronizationRect</string>
+ <string>126.IBPluginDependency</string>
+ <string>126.ImportedFromIB2</string>
+ <string>129.IBPluginDependency</string>
+ <string>129.ImportedFromIB2</string>
+ <string>130.IBPluginDependency</string>
+ <string>130.ImportedFromIB2</string>
+ <string>130.editorWindowContentRectSynchronizationRect</string>
+ <string>131.IBPluginDependency</string>
+ <string>131.ImportedFromIB2</string>
+ <string>134.IBPluginDependency</string>
+ <string>134.ImportedFromIB2</string>
+ <string>136.IBPluginDependency</string>
+ <string>136.ImportedFromIB2</string>
+ <string>143.IBPluginDependency</string>
+ <string>143.ImportedFromIB2</string>
+ <string>144.IBPluginDependency</string>
+ <string>144.ImportedFromIB2</string>
+ <string>145.IBPluginDependency</string>
+ <string>145.ImportedFromIB2</string>
+ <string>149.IBPluginDependency</string>
+ <string>149.ImportedFromIB2</string>
+ <string>150.IBPluginDependency</string>
+ <string>150.ImportedFromIB2</string>
+ <string>19.IBPluginDependency</string>
+ <string>19.ImportedFromIB2</string>
+ <string>195.IBPluginDependency</string>
+ <string>195.ImportedFromIB2</string>
+ <string>196.IBPluginDependency</string>
+ <string>196.ImportedFromIB2</string>
+ <string>197.IBPluginDependency</string>
+ <string>197.ImportedFromIB2</string>
+ <string>198.IBPluginDependency</string>
+ <string>198.ImportedFromIB2</string>
+ <string>199.IBPluginDependency</string>
+ <string>199.ImportedFromIB2</string>
+ <string>200.IBPluginDependency</string>
+ <string>200.ImportedFromIB2</string>
+ <string>200.editorWindowContentRectSynchronizationRect</string>
+ <string>201.IBPluginDependency</string>
+ <string>201.ImportedFromIB2</string>
+ <string>202.IBPluginDependency</string>
+ <string>202.ImportedFromIB2</string>
+ <string>203.IBPluginDependency</string>
+ <string>203.ImportedFromIB2</string>
+ <string>204.IBPluginDependency</string>
+ <string>204.ImportedFromIB2</string>
+ <string>205.IBPluginDependency</string>
+ <string>205.ImportedFromIB2</string>
+ <string>205.editorWindowContentRectSynchronizationRect</string>
+ <string>206.IBPluginDependency</string>
+ <string>206.ImportedFromIB2</string>
+ <string>207.IBPluginDependency</string>
+ <string>207.ImportedFromIB2</string>
+ <string>208.IBPluginDependency</string>
+ <string>208.ImportedFromIB2</string>
+ <string>209.IBPluginDependency</string>
+ <string>209.ImportedFromIB2</string>
+ <string>210.IBPluginDependency</string>
+ <string>210.ImportedFromIB2</string>
+ <string>211.IBPluginDependency</string>
+ <string>211.ImportedFromIB2</string>
+ <string>212.IBPluginDependency</string>
+ <string>212.ImportedFromIB2</string>
+ <string>212.editorWindowContentRectSynchronizationRect</string>
+ <string>213.IBPluginDependency</string>
+ <string>213.ImportedFromIB2</string>
+ <string>214.IBPluginDependency</string>
+ <string>214.ImportedFromIB2</string>
+ <string>215.IBPluginDependency</string>
+ <string>215.ImportedFromIB2</string>
+ <string>216.IBPluginDependency</string>
+ <string>216.ImportedFromIB2</string>
+ <string>217.IBPluginDependency</string>
+ <string>217.ImportedFromIB2</string>
+ <string>218.IBPluginDependency</string>
+ <string>218.ImportedFromIB2</string>
+ <string>219.IBPluginDependency</string>
+ <string>219.ImportedFromIB2</string>
+ <string>220.IBPluginDependency</string>
+ <string>220.ImportedFromIB2</string>
+ <string>220.editorWindowContentRectSynchronizationRect</string>
+ <string>221.IBPluginDependency</string>
+ <string>221.ImportedFromIB2</string>
+ <string>23.IBPluginDependency</string>
+ <string>23.ImportedFromIB2</string>
+ <string>236.IBPluginDependency</string>
+ <string>236.ImportedFromIB2</string>
+ <string>239.IBPluginDependency</string>
+ <string>239.ImportedFromIB2</string>
+ <string>24.IBPluginDependency</string>
+ <string>24.ImportedFromIB2</string>
+ <string>24.editorWindowContentRectSynchronizationRect</string>
+ <string>29.IBEditorWindowLastContentRect</string>
+ <string>29.IBPluginDependency</string>
+ <string>29.ImportedFromIB2</string>
+ <string>29.WindowOrigin</string>
+ <string>29.editorWindowContentRectSynchronizationRect</string>
+ <string>295.IBPluginDependency</string>
+ <string>296.IBPluginDependency</string>
+ <string>296.editorWindowContentRectSynchronizationRect</string>
+ <string>297.IBPluginDependency</string>
+ <string>298.IBPluginDependency</string>
+ <string>346.IBPluginDependency</string>
+ <string>346.ImportedFromIB2</string>
+ <string>348.IBPluginDependency</string>
+ <string>348.ImportedFromIB2</string>
+ <string>349.IBPluginDependency</string>
+ <string>349.ImportedFromIB2</string>
+ <string>349.editorWindowContentRectSynchronizationRect</string>
+ <string>350.IBPluginDependency</string>
+ <string>350.ImportedFromIB2</string>
+ <string>351.IBPluginDependency</string>
+ <string>351.ImportedFromIB2</string>
+ <string>354.IBPluginDependency</string>
+ <string>354.ImportedFromIB2</string>
+ <string>371.IBEditorWindowLastContentRect</string>
+ <string>371.IBWindowTemplateEditedContentRect</string>
+ <string>371.NSWindowTemplate.visibleAtLaunch</string>
+ <string>371.editorWindowContentRectSynchronizationRect</string>
+ <string>371.windowTemplate.maxSize</string>
+ <string>372.IBPluginDependency</string>
+ <string>375.IBPluginDependency</string>
+ <string>376.IBEditorWindowLastContentRect</string>
+ <string>376.IBPluginDependency</string>
+ <string>377.IBPluginDependency</string>
+ <string>378.IBPluginDependency</string>
+ <string>379.IBPluginDependency</string>
+ <string>380.IBPluginDependency</string>
+ <string>381.IBPluginDependency</string>
+ <string>382.IBPluginDependency</string>
+ <string>383.IBPluginDependency</string>
+ <string>384.IBPluginDependency</string>
+ <string>385.IBPluginDependency</string>
+ <string>386.IBPluginDependency</string>
+ <string>387.IBPluginDependency</string>
+ <string>388.IBEditorWindowLastContentRect</string>
+ <string>388.IBPluginDependency</string>
+ <string>389.IBPluginDependency</string>
+ <string>390.IBPluginDependency</string>
+ <string>391.IBPluginDependency</string>
+ <string>392.IBPluginDependency</string>
+ <string>393.IBPluginDependency</string>
+ <string>394.IBPluginDependency</string>
+ <string>395.IBPluginDependency</string>
+ <string>396.IBPluginDependency</string>
+ <string>397.IBPluginDependency</string>
+ <string>398.IBPluginDependency</string>
+ <string>399.IBPluginDependency</string>
+ <string>400.IBPluginDependency</string>
+ <string>401.IBPluginDependency</string>
+ <string>402.IBPluginDependency</string>
+ <string>403.IBPluginDependency</string>
+ <string>404.IBPluginDependency</string>
+ <string>405.IBPluginDependency</string>
+ <string>406.IBPluginDependency</string>
+ <string>407.IBPluginDependency</string>
+ <string>408.IBPluginDependency</string>
+ <string>409.IBPluginDependency</string>
+ <string>410.IBPluginDependency</string>
+ <string>411.IBPluginDependency</string>
+ <string>412.IBPluginDependency</string>
+ <string>413.IBPluginDependency</string>
+ <string>414.IBPluginDependency</string>
+ <string>415.IBPluginDependency</string>
+ <string>416.IBPluginDependency</string>
+ <string>417.IBPluginDependency</string>
+ <string>418.IBPluginDependency</string>
+ <string>419.IBPluginDependency</string>
+ <string>420.IBPluginDependency</string>
+ <string>450.IBPluginDependency</string>
+ <string>452.IBPluginDependency</string>
+ <string>453.IBPluginDependency</string>
+ <string>5.IBPluginDependency</string>
+ <string>5.ImportedFromIB2</string>
+ <string>56.IBPluginDependency</string>
+ <string>56.ImportedFromIB2</string>
+ <string>57.IBEditorWindowLastContentRect</string>
+ <string>57.IBPluginDependency</string>
+ <string>57.ImportedFromIB2</string>
+ <string>57.editorWindowContentRectSynchronizationRect</string>
+ <string>58.IBPluginDependency</string>
+ <string>58.ImportedFromIB2</string>
+ <string>72.IBPluginDependency</string>
+ <string>72.ImportedFromIB2</string>
+ <string>73.IBPluginDependency</string>
+ <string>73.ImportedFromIB2</string>
+ <string>74.IBPluginDependency</string>
+ <string>74.ImportedFromIB2</string>
+ <string>75.IBPluginDependency</string>
+ <string>75.ImportedFromIB2</string>
+ <string>77.IBPluginDependency</string>
+ <string>77.ImportedFromIB2</string>
+ <string>78.IBPluginDependency</string>
+ <string>78.ImportedFromIB2</string>
+ <string>79.IBPluginDependency</string>
+ <string>79.ImportedFromIB2</string>
+ <string>80.IBPluginDependency</string>
+ <string>80.ImportedFromIB2</string>
+ <string>81.IBPluginDependency</string>
+ <string>81.ImportedFromIB2</string>
+ <string>81.editorWindowContentRectSynchronizationRect</string>
+ <string>82.IBPluginDependency</string>
+ <string>82.ImportedFromIB2</string>
+ <string>83.IBPluginDependency</string>
+ <string>83.ImportedFromIB2</string>
+ <string>92.IBPluginDependency</string>
+ <string>92.ImportedFromIB2</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>com.apple.InterfaceBuilderKit</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="1" id="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{596, 852}, {216, 23}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{522, 812}, {146, 23}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{436, 809}, {64, 6}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{608, 612}, {275, 83}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{187, 434}, {243, 243}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{608, 612}, {167, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{608, 612}, {241, 103}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{525, 802}, {197, 73}}</string>
+ <string>{{57, 934}, {478, 20}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{74, 862}</string>
+ <string>{{6, 978}, {478, 20}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{475, 832}, {234, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{608, 612}, {215, 63}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{157, 483}, {480, 360}}</string>
+ <string>{{157, 483}, {480, 360}}</string>
+ <reference ref="9"/>
+ <string>{{33, 99}, {480, 360}}</string>
+ <string>{3.40282e+38, 3.40282e+38}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{437, 242}, {86, 43}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{523, 2}, {178, 283}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{69, 751}, {245, 183}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{23, 794}, {245, 183}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>{{145, 474}, {199, 203}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <reference ref="9"/>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">454</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">AppController</string>
+ <string key="superclassName">NSObject</string>
+ <object class="NSMutableDictionary" key="actions">
+ <string key="NS.key.0">printCert:</string>
+ <string key="NS.object.0">id</string>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">AppController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSObject</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">../AsyncSocket.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../CertTest.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.${PRODUCT_NAME:identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
--- /dev/null
+//
+// X509Certificate.h
+//
+// This class is in the public domain.
+// Originally created by Robbie Hanson on Mon Jan 26 2009.
+// Updated and maintained by Deusty Designs and the Mac development community.
+//
+// http://code.google.com/p/cocoaasyncsocket/
+//
+// This class is largely derived from Apple's sample code project: SSLSample.
+// This class does not extract every bit of available information, just the most common fields.
+
+#import <Foundation/Foundation.h>
+@class AsyncSocket;
+
+// Top Level Keys
+#define X509_ISSUER @"Issuer"
+#define X509_SUBJECT @"Subject"
+#define X509_NOT_VALID_BEFORE @"NotValidBefore"
+#define X509_NOT_VALID_AFTER @"NotValidAfter"
+#define X509_PUBLIC_KEY @"PublicKey"
+#define X509_SERIAL_NUMBER @"SerialNumber"
+
+// Keys For Issuer/Subject Dictionaries
+#define X509_COUNTRY @"Country"
+#define X509_ORGANIZATION @"Organization"
+#define X509_LOCALITY @"Locality"
+#define X509_ORANIZATIONAL_UNIT @"OrganizationalUnit"
+#define X509_COMMON_NAME @"CommonName"
+#define X509_SURNAME @"Surname"
+#define X509_TITLE @"Title"
+#define X509_STATE_PROVINCE @"StateProvince"
+#define X509_COLLECTIVE_STATE_PROVINCE @"CollectiveStateProvince"
+#define X509_EMAIL_ADDRESS @"EmailAddress"
+#define X509_STREET_ADDRESS @"StreetAddress"
+#define X509_POSTAL_CODE @"PostalCode"
+#define X509_OTHERS @"Others"
+
+@interface X509Certificate : NSObject
+
++ (NSDictionary *)extractCertDictFromAsyncSocket:(AsyncSocket *)socket;
++ (NSDictionary *)extractCertDictFromReadStream:(CFReadStreamRef)readStream;
++ (NSDictionary *)extractCertDictFromIdentity:(SecIdentityRef)identity;
++ (NSDictionary *)extractCertDictFromCert:(SecCertificateRef)cert;
+
+@end
--- /dev/null
+//
+// X509Certificate.m
+//
+// This class is in the public domain.
+// Originally created by Robbie Hanson on Mon Jan 26 2009.
+// Updated and maintained by Deusty Designs and the Mac development community.
+//
+// http://code.google.com/p/cocoaasyncsocket/
+//
+// This class is largely derived from Apple's sample code project: SSLSample.
+// This class does not extract every bit of available information, just the most common fields.
+
+#import "X509Certificate.h"
+#import "AsyncSocket.h"
+#import <Security/Security.h>
+
+#define UTC_TIME_STRLEN 13
+#define GENERALIZED_TIME_STRLEN 15
+
+
+@implementation X509Certificate
+
+// Standard app-level memory functions required by CDSA
+
+static void * appMalloc (uint32 size, void *allocRef)
+{
+ return malloc(size);
+}
+static void * appCalloc(uint32 num, uint32 size, void *allocRef)
+{
+ return calloc(num, size);
+}
+static void * appRealloc (void *ptr, uint32 size, void *allocRef)
+{
+ return realloc(ptr, size);
+}
+static void appFree (void *mem_ptr, void *allocRef)
+{
+ free(mem_ptr);
+}
+
+
+static const CSSM_API_MEMORY_FUNCS memFuncs = {
+ (CSSM_MALLOC)appMalloc,
+ (CSSM_FREE)appFree,
+ (CSSM_REALLOC)appRealloc,
+ (CSSM_CALLOC)appCalloc,
+ NULL
+};
+
+static const CSSM_VERSION vers = {2, 0};
+static const CSSM_GUID testGuid = { 0xFADE, 0, 0, { 1,2,3,4,5,6,7,0 }};
+
+static BOOL CSSMStartup()
+{
+ CSSM_RETURN crtn;
+ CSSM_PVC_MODE pvcPolicy = CSSM_PVC_NONE;
+
+ crtn = CSSM_Init (&vers,
+ CSSM_PRIVILEGE_SCOPE_NONE,
+ &testGuid,
+ CSSM_KEY_HIERARCHY_NONE,
+ &pvcPolicy,
+ NULL /* reserved */);
+
+ if(crtn != CSSM_OK)
+ {
+ cssmPerror("CSSM_Init", crtn);
+ return NO;
+ }
+ else
+ {
+ return YES;
+ }
+}
+
+static CSSM_CL_HANDLE CLStartup()
+{
+ CSSM_CL_HANDLE clHandle;
+ CSSM_RETURN crtn;
+
+ if(CSSMStartup() == NO)
+ {
+ return 0;
+ }
+
+ crtn = CSSM_ModuleLoad(&gGuidAppleX509CL,
+ CSSM_KEY_HIERARCHY_NONE,
+ NULL, // eventHandler
+ NULL); // AppNotifyCallbackCtx
+ if(crtn != CSSM_OK)
+ {
+ cssmPerror("CSSM_ModuleLoad", crtn);
+ return 0;
+ }
+
+ crtn = CSSM_ModuleAttach (&gGuidAppleX509CL,
+ &vers,
+ &memFuncs, // memFuncs
+ 0, // SubserviceID
+ CSSM_SERVICE_CL, // SubserviceFlags - Where is this used?
+ 0, // AttachFlags
+ CSSM_KEY_HIERARCHY_NONE,
+ NULL, // FunctionTable
+ 0, // NumFuncTable
+ NULL, // reserved
+ &clHandle);
+ if(crtn != CSSM_OK)
+ {
+ cssmPerror("CSSM_ModuleAttach", crtn);
+ return 0;
+ }
+
+ return clHandle;
+}
+
+static void CLShutdown(CSSM_CL_HANDLE clHandle)
+{
+ CSSM_RETURN crtn;
+
+ crtn = CSSM_ModuleDetach(clHandle);
+ if(crtn != CSSM_OK)
+ {
+ cssmPerror("CSSM_ModuleDetach", crtn);
+ }
+
+ crtn = CSSM_ModuleUnload(&gGuidAppleX509CL, NULL, NULL);
+ if(crtn != CSSM_OK)
+ {
+ cssmPerror("CSSM_ModuleUnload", crtn);
+ }
+}
+
+static BOOL CompareCSSMData(const CSSM_DATA *d1, const CSSM_DATA *d2)
+{
+ if(d1 == NULL || d2 == NULL)
+ {
+ return NO;
+ }
+ if(d1->Length != d2->Length)
+ {
+ return NO;
+ }
+
+ return memcmp(d1->Data, d2->Data, d1->Length) == 0;
+}
+
+static BOOL CompareOids(const CSSM_OID *oid1, const CSSM_OID *oid2)
+{
+ if(oid1 == NULL || oid2 == NULL)
+ {
+ return NO;
+ }
+ if(oid1->Length != oid2->Length)
+ {
+ return NO;
+ }
+
+ return memcmp(oid1->Data, oid2->Data, oid1->Length) == 0;
+}
+
+static NSString* KeyForOid(const CSSM_OID *oid)
+{
+ if(CompareOids(oid, &CSSMOID_CountryName))
+ {
+ return X509_COUNTRY;
+ }
+ if(CompareOids(oid, &CSSMOID_OrganizationName))
+ {
+ return X509_ORGANIZATION;
+ }
+ if(CompareOids(oid, &CSSMOID_LocalityName))
+ {
+ return X509_LOCALITY;
+ }
+ if(CompareOids(oid, &CSSMOID_OrganizationalUnitName))
+ {
+ return X509_ORANIZATIONAL_UNIT;
+ }
+ if(CompareOids(oid, &CSSMOID_CommonName))
+ {
+ return X509_COMMON_NAME;
+ }
+ if(CompareOids(oid, &CSSMOID_Surname))
+ {
+ return X509_SURNAME;
+ }
+ if(CompareOids(oid, &CSSMOID_Title))
+ {
+ return X509_TITLE;
+ }
+ if(CompareOids(oid, &CSSMOID_StateProvinceName))
+ {
+ return X509_STATE_PROVINCE;
+ }
+ if(CompareOids(oid, &CSSMOID_CollectiveStateProvinceName))
+ {
+ return X509_COLLECTIVE_STATE_PROVINCE;
+ }
+ if(CompareOids(oid, &CSSMOID_EmailAddress))
+ {
+ return X509_EMAIL_ADDRESS;
+ }
+ if(CompareOids(oid, &CSSMOID_StreetAddress))
+ {
+ return X509_STREET_ADDRESS;
+ }
+ if(CompareOids(oid, &CSSMOID_PostalCode))
+ {
+ return X509_POSTAL_CODE;
+ }
+
+ // Not every possible Oid is checked for.
+ // Feel free to add any you may need.
+ // They are listed in the Security Framework's aoisattr.h file.
+
+ return nil;
+}
+
+static NSString* DataToString(const CSSM_DATA *data, const CSSM_BER_TAG *type)
+{
+ NSStringEncoding encoding;
+ switch (*type)
+ {
+ case BER_TAG_PRINTABLE_STRING :
+ case BER_TAG_TELETEX_STRING :
+
+ encoding = NSISOLatin1StringEncoding;
+ break;
+
+ case BER_TAG_PKIX_BMP_STRING :
+ case BER_TAG_PKIX_UNIVERSAL_STRING :
+ case BER_TAG_PKIX_UTF8_STRING :
+
+ encoding = NSUTF8StringEncoding;
+ break;
+
+ default :
+ return nil;
+ }
+
+ NSString *result = [[NSString alloc] initWithBytes:data->Data
+ length:data->Length
+ encoding:encoding];
+ return [result autorelease];
+}
+
+static NSDate* TimeToDate(const char *str, unsigned len)
+{
+ BOOL isUTC;
+ unsigned i;
+ long year, month, day, hour, minute, second;
+
+ // Check for null or empty strings
+ if(str == NULL || len == 0)
+ {
+ return nil;
+ }
+
+ // Ignore NULL termination
+ if(str[len - 1] == '\0')
+ {
+ len--;
+ }
+
+ // Check for proper string length
+ if(len == UTC_TIME_STRLEN)
+ {
+ // 2-digit year, not Y2K compliant
+ isUTC = YES;
+ }
+ else if(len == GENERALIZED_TIME_STRLEN)
+ {
+ // 4-digit year
+ isUTC = NO;
+ }
+ else
+ {
+ // Unknown format
+ return nil;
+ }
+
+ // Check that all characters except last are digits
+ for(i = 0; i < (len - 1); i++)
+ {
+ if(!(isdigit(str[i])))
+ {
+ return nil;
+ }
+ }
+
+ // Check last character is a 'Z'
+ if(str[len - 1] != 'Z' )
+ {
+ return nil;
+ }
+
+ // Start parsing
+ i = 0;
+ char tmp[5];
+
+ // Year
+ if(isUTC)
+ {
+ tmp[0] = str[i++];
+ tmp[1] = str[i++];
+ tmp[2] = '\0';
+
+ year = strtol(tmp, NULL, 10);
+
+ // 2-digit year:
+ // 0 <= year < 50 : assume century 21
+ // 50 <= year < 70 : illegal per PKIX
+ // 70 < year <= 99 : assume century 20
+
+ if(year < 50)
+ {
+ year += 2000;
+ }
+ else if(year < 70)
+ {
+ return nil;
+ }
+ else
+ {
+ year += 1900;
+ }
+ }
+ else
+ {
+ tmp[0] = str[i++];
+ tmp[1] = str[i++];
+ tmp[2] = str[i++];
+ tmp[3] = str[i++];
+ tmp[4] = '\0';
+
+ year = strtol(tmp, NULL, 10);
+ }
+
+ // Month
+ tmp[0] = str[i++];
+ tmp[1] = str[i++];
+ tmp[2] = '\0';
+
+ month = strtol(tmp, NULL, 10);
+
+ // Months are represented in format from 1 to 12
+ if(month > 12 || month <= 0)
+ {
+ return nil;
+ }
+
+ // Day
+ tmp[0] = str[i++];
+ tmp[1] = str[i++];
+ tmp[2] = '\0';
+
+ day = strtol(tmp, NULL, 10);
+
+ // Days are represented in format from 1 to 31
+ if(day > 31 || day <= 0)
+ {
+ return nil;
+ }
+
+ // Hour
+ tmp[0] = str[i++];
+ tmp[1] = str[i++];
+ tmp[2] = '\0';
+
+ hour = strtol(tmp, NULL, 10);
+
+ // Hours are represented in format from 0 to 23
+ if(hour > 23 || hour < 0)
+ {
+ return nil;
+ }
+
+ // Minute
+ tmp[0] = str[i++];
+ tmp[1] = str[i++];
+ tmp[2] = '\0';
+
+ minute = strtol(tmp, NULL, 10);
+
+ // Minutes are represented in format from 0 to 59
+ if(minute > 59 || minute < 0)
+ {
+ return nil;
+ }
+
+ // Second
+ tmp[0] = str[i++];
+ tmp[1] = str[i++];
+ tmp[2] = '\0';
+
+ second = strtol(tmp, NULL, 10);
+
+ // Seconds are represented in format from 0 to 59
+ if(second > 59 || second < 0)
+ {
+ return nil;
+ }
+
+ CFGregorianDate gDate = { year, month, day, hour, minute, second };
+ CFAbsoluteTime aTime = CFGregorianDateGetAbsoluteTime(gDate, NULL);
+
+ return [NSDate dateWithTimeIntervalSinceReferenceDate:aTime];
+}
+
+static NSData* RawToData(const CSSM_DATA *data)
+{
+ if(data == NULL)
+ {
+ return nil;
+ }
+
+ return [NSData dataWithBytes:data->Data length:data->Length];
+}
+
+static NSDictionary* X509NameToDictionary(const CSSM_X509_NAME *x509Name)
+{
+ if(x509Name == NULL)
+ {
+ return nil;
+ }
+
+ NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:6];
+ NSMutableArray *others = [NSMutableArray arrayWithCapacity:6];
+
+ UInt32 i, j;
+ for(i = 0; i < x509Name->numberOfRDNs; i++)
+ {
+ const CSSM_X509_RDN *name = &x509Name->RelativeDistinguishedName[i];
+
+ for(j = 0; j < name->numberOfPairs; j++)
+ {
+ const CSSM_X509_TYPE_VALUE_PAIR *pair = &name->AttributeTypeAndValue[j];
+
+ NSString *value = DataToString(&pair->value, &pair->valueType);
+ if(value)
+ {
+ NSString *key = KeyForOid(&pair->type);
+ if(key)
+ [result setObject:value forKey:key];
+ else
+ [others addObject:value];
+ }
+ }
+ }
+
+ if([others count] > 0)
+ {
+ [result setObject:others forKey:X509_OTHERS];
+ }
+
+ return result;
+}
+
+static void AddCSSMField(const CSSM_FIELD *field, NSMutableDictionary *dict)
+{
+ const CSSM_DATA *fieldData = &field->FieldValue;
+ const CSSM_OID *fieldOid = &field->FieldOid;
+
+ if(CompareOids(fieldOid, &CSSMOID_X509V1SerialNumber))
+ {
+ NSData *data = RawToData(fieldData);
+ if(data)
+ {
+ [dict setObject:data forKey:X509_SERIAL_NUMBER];
+ }
+ }
+ else if(CompareOids(fieldOid, &CSSMOID_X509V1IssuerNameCStruct))
+ {
+ CSSM_X509_NAME_PTR issuer = (CSSM_X509_NAME_PTR)fieldData->Data;
+ if(issuer && fieldData->Length == sizeof(CSSM_X509_NAME))
+ {
+ NSDictionary *issuerDict = X509NameToDictionary(issuer);
+ if(issuerDict)
+ {
+ [dict setObject:issuerDict forKey:X509_ISSUER];
+ }
+ }
+ }
+ else if(CompareOids(fieldOid, &CSSMOID_X509V1SubjectNameCStruct))
+ {
+ CSSM_X509_NAME_PTR subject = (CSSM_X509_NAME_PTR)fieldData->Data;
+ if(subject && fieldData->Length == sizeof(CSSM_X509_NAME))
+ {
+ NSDictionary *subjectDict = X509NameToDictionary(subject);
+ if(subjectDict)
+ {
+ [dict setObject:subjectDict forKey:X509_SUBJECT];
+ }
+ }
+ }
+ else if(CompareOids(fieldOid, &CSSMOID_X509V1ValidityNotBefore))
+ {
+ CSSM_X509_TIME_PTR time = (CSSM_X509_TIME_PTR)fieldData->Data;
+ if(time && fieldData->Length == sizeof(CSSM_X509_TIME))
+ {
+ NSDate *date = TimeToDate((const char *)time->time.Data, time->time.Length);
+ if(date)
+ {
+ [dict setObject:date forKey:X509_NOT_VALID_BEFORE];
+ }
+ }
+ }
+ else if(CompareOids(fieldOid, &CSSMOID_X509V1ValidityNotAfter))
+ {
+ CSSM_X509_TIME_PTR time = (CSSM_X509_TIME_PTR)fieldData->Data;
+ if(time && fieldData->Length == sizeof(CSSM_X509_TIME))
+ {
+ NSDate *date = TimeToDate((const char *)time->time.Data, time->time.Length);
+ if(date)
+ {
+ [dict setObject:date forKey:X509_NOT_VALID_AFTER];
+ }
+ }
+ }
+ else if(CompareOids(fieldOid, &CSSMOID_X509V1SubjectPublicKeyCStruct))
+ {
+ CSSM_X509_SUBJECT_PUBLIC_KEY_INFO_PTR pubKeyInfo = (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO_PTR)fieldData->Data;
+ if(pubKeyInfo && fieldData->Length == sizeof(CSSM_X509_SUBJECT_PUBLIC_KEY_INFO))
+ {
+ NSData *data = RawToData(&pubKeyInfo->subjectPublicKey);
+ if(data)
+ {
+ [dict setObject:data forKey:X509_PUBLIC_KEY];
+ }
+ }
+ }
+}
+
++ (NSDictionary *)extractCertDictFromAsyncSocket:(AsyncSocket *)socket
+{
+ if(socket == nil)
+ {
+ return nil;
+ }
+
+ return [self extractCertDictFromReadStream:[socket getCFReadStream]];
+}
+
++ (NSDictionary *)extractCertDictFromReadStream:(CFReadStreamRef)readStream
+{
+ if(readStream == NULL)
+ {
+ return nil;
+ }
+
+ NSDictionary *result = nil;
+
+ CFArrayRef certs = CFReadStreamCopyProperty(readStream, kCFStreamPropertySSLPeerCertificates);
+ if(certs && CFArrayGetCount(certs) > 0)
+ {
+ // The first cert in the chain is the subject cert
+ SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, 0);
+
+ result = [self extractCertDictFromCert:cert];
+ }
+
+ if(certs) CFRelease(certs);
+
+ return result;
+}
+
++ (NSDictionary *)extractCertDictFromIdentity:(SecIdentityRef)identity
+{
+ if(identity == NULL)
+ {
+ return nil;
+ }
+
+ NSDictionary *result = nil;
+ SecCertificateRef cert = NULL;
+
+ OSStatus err = SecIdentityCopyCertificate(identity, &cert);
+ if(err)
+ {
+ cssmPerror("SecIdentityCopyCertificate", err);
+ return nil;
+ }
+ else
+ {
+ result = [self extractCertDictFromCert:cert];
+ }
+
+ if(cert) CFRelease(cert);
+
+ return result;
+}
+
++ (NSDictionary *)extractCertDictFromCert:(SecCertificateRef)cert
+{
+ CSSM_CL_HANDLE clHandle = CLStartup();
+ if(clHandle == 0)
+ {
+ return nil;
+ }
+
+ NSMutableDictionary *result = nil;
+
+ CSSM_DATA certData;
+ if(SecCertificateGetData(cert, &certData) == noErr)
+ {
+ uint32 i;
+ uint32 numFields;
+ CSSM_FIELD_PTR fieldPtr;
+
+ CSSM_RETURN crtn = CSSM_CL_CertGetAllFields(clHandle, &certData, &numFields, &fieldPtr);
+ if(crtn == CSSM_OK)
+ {
+ result = [NSMutableDictionary dictionaryWithCapacity:6];
+
+ for(i = 0; i < numFields; i++)
+ {
+ AddCSSMField(&fieldPtr[i], result);
+ }
+
+ CSSM_CL_FreeFields(clHandle, numFields, &fieldPtr);
+ }
+ }
+
+ CLShutdown(clHandle);
+
+ return result;
+}
+
+@end
--- /dev/null
+//
+// main.m
+// CertTest
+//
+// Created by Robbie Hanson on 1/26/09.
+// Copyright Deusty Designs, LLC. 2009. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, char *argv[])
+{
+ return NSApplicationMain(argc, (const char **) argv);
+}
--- /dev/null
+#import <Cocoa/Cocoa.h>
+
+@class AsyncSocket;
+
+@interface AppController : NSObject
+{
+ AsyncSocket *listenSocket;
+ NSMutableArray *connectedSockets;
+
+ BOOL isRunning;
+
+ IBOutlet id logView;
+ IBOutlet id portField;
+ IBOutlet id startStopButton;
+}
+- (IBAction)startStop:(id)sender;
+@end
--- /dev/null
+#import "AppController.h"
+#import "AsyncSocket.h"
+
+#define WELCOME_MSG 0
+#define ECHO_MSG 1
+
+#define FORMAT(format, ...) [NSString stringWithFormat:(format), ##__VA_ARGS__]
+
+@interface AppController (PrivateAPI)
+- (void)logError:(NSString *)msg;
+- (void)logInfo:(NSString *)msg;
+- (void)logMessage:(NSString *)msg;
+@end
+
+
+@implementation AppController
+
+- (id)init
+{
+ if((self = [super init]))
+ {
+ listenSocket = [[AsyncSocket alloc] initWithDelegate:self];
+ connectedSockets = [[NSMutableArray alloc] initWithCapacity:1];
+
+ isRunning = NO;
+ }
+ return self;
+}
+
+- (void)awakeFromNib
+{
+ [logView setString:@""];
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
+{
+ NSLog(@"Ready");
+
+ // Advanced options - enable the socket to contine operations even during modal dialogs, and menu browsing
+ [listenSocket setRunLoopModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
+}
+
+- (void)scrollToBottom
+{
+ NSScrollView *scrollView = [logView enclosingScrollView];
+ NSPoint newScrollOrigin;
+
+ if ([[scrollView documentView] isFlipped])
+ newScrollOrigin = NSMakePoint(0.0F, NSMaxY([[scrollView documentView] frame]));
+ else
+ newScrollOrigin = NSMakePoint(0.0F, 0.0F);
+
+ [[scrollView documentView] scrollPoint:newScrollOrigin];
+}
+
+- (void)logError:(NSString *)msg
+{
+ NSString *paragraph = [NSString stringWithFormat:@"%@\n", msg];
+
+ NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:1];
+ [attributes setObject:[NSColor redColor] forKey:NSForegroundColorAttributeName];
+
+ NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes];
+ [as autorelease];
+
+ [[logView textStorage] appendAttributedString:as];
+ [self scrollToBottom];
+}
+
+- (void)logInfo:(NSString *)msg
+{
+ NSString *paragraph = [NSString stringWithFormat:@"%@\n", msg];
+
+ NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:1];
+ [attributes setObject:[NSColor purpleColor] forKey:NSForegroundColorAttributeName];
+
+ NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes];
+ [as autorelease];
+
+ [[logView textStorage] appendAttributedString:as];
+ [self scrollToBottom];
+}
+
+- (void)logMessage:(NSString *)msg
+{
+ NSString *paragraph = [NSString stringWithFormat:@"%@\n", msg];
+
+ NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:1];
+ [attributes setObject:[NSColor blackColor] forKey:NSForegroundColorAttributeName];
+
+ NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes];
+ [as autorelease];
+
+ [[logView textStorage] appendAttributedString:as];
+ [self scrollToBottom];
+}
+
+- (IBAction)startStop:(id)sender
+{
+ if(!isRunning)
+ {
+ int port = [portField intValue];
+
+ if(port < 0 || port > 65535)
+ {
+ port = 0;
+ }
+
+ NSError *error = nil;
+ if(![listenSocket acceptOnPort:port error:&error])
+ {
+ [self logError:FORMAT(@"Error starting server: %@", error)];
+ return;
+ }
+
+ [self logInfo:FORMAT(@"Echo server started on port %hu", [listenSocket localPort])];
+ isRunning = YES;
+
+ [portField setEnabled:NO];
+ [startStopButton setTitle:@"Stop"];
+ }
+ else
+ {
+ // Stop accepting connections
+ [listenSocket disconnect];
+
+ // Stop any client connections
+ int i;
+ for(i = 0; i < [connectedSockets count]; i++)
+ {
+ // Call disconnect on the socket,
+ // which will invoke the onSocketDidDisconnect: method,
+ // which will remove the socket from the list.
+ [[connectedSockets objectAtIndex:i] disconnect];
+ }
+
+ [self logInfo:@"Stopped Echo server"];
+ isRunning = false;
+
+ [portField setEnabled:YES];
+ [startStopButton setTitle:@"Start"];
+ }
+}
+
+- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
+{
+ [connectedSockets addObject:newSocket];
+}
+
+- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
+{
+ [self logInfo:FORMAT(@"Accepted client %@:%hu", host, port)];
+
+ NSString *welcomeMsg = @"Welcome to the AsyncSocket Echo Server\r\n";
+ NSData *welcomeData = [welcomeMsg dataUsingEncoding:NSUTF8StringEncoding];
+
+ [sock writeData:welcomeData withTimeout:-1 tag:WELCOME_MSG];
+
+ // We could call readDataToData:withTimeout:tag: here - that would be perfectly fine.
+ // If we did this, we'd want to add a check in onSocket:didWriteDataWithTag: and only
+ // queue another read if tag != WELCOME_MSG.
+}
+
+- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
+{
+ [sock readDataToData:[AsyncSocket CRLFData] withTimeout:-1 tag:0];
+}
+
+- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
+{
+ NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length] - 2)];
+ NSString *msg = [[[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding] autorelease];
+ if(msg)
+ {
+ [self logMessage:msg];
+ }
+ else
+ {
+ [self logError:@"Error converting received data into UTF-8 String"];
+ }
+
+ // Even if we were unable to write the incoming data to the log,
+ // we're still going to echo it back to the client.
+ [sock writeData:data withTimeout:-1 tag:ECHO_MSG];
+}
+
+- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
+{
+ [self logInfo:FORMAT(@"Client Disconnected: %@:%hu", [sock connectedHost], [sock connectedPort])];
+}
+
+- (void)onSocketDidDisconnect:(AsyncSocket *)sock
+{
+ [connectedSockets removeObject:sock];
+}
+
+@end
--- /dev/null
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 44;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; };
+ 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
+ 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
+ 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
+ DC50DAB60E4673910071BAD9 /* AsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = DC50DAB50E4673910071BAD9 /* AsyncSocket.m */; };
+ DC7619050E26F49700A77CA5 /* AppController.m in Sources */ = {isa = PBXBuildFile; fileRef = DC7619040E26F49700A77CA5 /* AppController.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
+ 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
+ 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 29B97319FDCFA39411CA2CEA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = "<group>"; };
+ 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
+ 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+ 32CA4F630368D1EE00C91783 /* EchoServer_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EchoServer_Prefix.pch; sourceTree = "<group>"; };
+ 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 8D1107320486CEB800E47090 /* EchoServer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EchoServer.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ DC50DAB40E4673830071BAD9 /* AsyncSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AsyncSocket.h; path = ../AsyncSocket.h; sourceTree = SOURCE_ROOT; };
+ DC50DAB50E4673910071BAD9 /* AsyncSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AsyncSocket.m; path = ../AsyncSocket.m; sourceTree = SOURCE_ROOT; };
+ DC7619030E26F49700A77CA5 /* AppController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppController.h; sourceTree = "<group>"; };
+ DC7619040E26F49700A77CA5 /* AppController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppController.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8D11072E0486CEB800E47090 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 080E96DDFE201D6D7F000001 /* Classes */ = {
+ isa = PBXGroup;
+ children = (
+ DC50DAB40E4673830071BAD9 /* AsyncSocket.h */,
+ DC50DAB50E4673910071BAD9 /* AsyncSocket.m */,
+ DC7619030E26F49700A77CA5 /* AppController.h */,
+ DC7619040E26F49700A77CA5 /* AppController.m */,
+ );
+ name = Classes;
+ sourceTree = "<group>";
+ };
+ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
+ );
+ name = "Linked Frameworks";
+ sourceTree = "<group>";
+ };
+ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 29B97324FDCFA39411CA2CEA /* AppKit.framework */,
+ 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
+ 29B97325FDCFA39411CA2CEA /* Foundation.framework */,
+ );
+ name = "Other Frameworks";
+ sourceTree = "<group>";
+ };
+ 19C28FACFE9D520D11CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8D1107320486CEB800E47090 /* EchoServer.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 29B97314FDCFA39411CA2CEA /* EchoServer */ = {
+ isa = PBXGroup;
+ children = (
+ 080E96DDFE201D6D7F000001 /* Classes */,
+ 29B97315FDCFA39411CA2CEA /* Other Sources */,
+ 29B97317FDCFA39411CA2CEA /* Resources */,
+ 29B97323FDCFA39411CA2CEA /* Frameworks */,
+ 19C28FACFE9D520D11CA2CBB /* Products */,
+ );
+ name = EchoServer;
+ sourceTree = "<group>";
+ };
+ 29B97315FDCFA39411CA2CEA /* Other Sources */ = {
+ isa = PBXGroup;
+ children = (
+ 32CA4F630368D1EE00C91783 /* EchoServer_Prefix.pch */,
+ 29B97316FDCFA39411CA2CEA /* main.m */,
+ );
+ name = "Other Sources";
+ sourceTree = "<group>";
+ };
+ 29B97317FDCFA39411CA2CEA /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 8D1107310486CEB800E47090 /* Info.plist */,
+ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
+ 29B97318FDCFA39411CA2CEA /* MainMenu.nib */,
+ );
+ name = Resources;
+ sourceTree = "<group>";
+ };
+ 29B97323FDCFA39411CA2CEA /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
+ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 8D1107260486CEB800E47090 /* EchoServer */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "EchoServer" */;
+ buildPhases = (
+ 8D1107290486CEB800E47090 /* Resources */,
+ 8D11072C0486CEB800E47090 /* Sources */,
+ 8D11072E0486CEB800E47090 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = EchoServer;
+ productInstallPath = "$(HOME)/Applications";
+ productName = EchoServer;
+ productReference = 8D1107320486CEB800E47090 /* EchoServer.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 29B97313FDCFA39411CA2CEA /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "EchoServer" */;
+ compatibilityVersion = "Xcode 3.0";
+ hasScannedForEncodings = 1;
+ mainGroup = 29B97314FDCFA39411CA2CEA /* EchoServer */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 8D1107260486CEB800E47090 /* EchoServer */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 8D1107290486CEB800E47090 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */,
+ 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8D11072C0486CEB800E47090 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D11072D0486CEB800E47090 /* main.m in Sources */,
+ DC7619050E26F49700A77CA5 /* AppController.m in Sources */,
+ DC50DAB60E4673910071BAD9 /* AsyncSocket.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 089C165DFE840E0CC02AAC07 /* English */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ 29B97318FDCFA39411CA2CEA /* MainMenu.nib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 29B97319FDCFA39411CA2CEA /* English */,
+ );
+ name = MainMenu.nib;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ C01FCF4B08A954540054247B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = EchoServer_Prefix.pch;
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ PRODUCT_NAME = EchoServer;
+ WRAPPER_EXTENSION = app;
+ ZERO_LINK = YES;
+ };
+ name = Debug;
+ };
+ C01FCF4C08A954540054247B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = EchoServer_Prefix.pch;
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ PRODUCT_NAME = EchoServer;
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+ C01FCF4F08A954540054247B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_MISSING_PARENTHESES = YES;
+ GCC_WARN_SHADOW = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ PREBINDING = NO;
+ SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
+ };
+ name = Debug;
+ };
+ C01FCF5008A954540054247B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = (
+ ppc,
+ i386,
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_MISSING_PARENTHESES = YES;
+ GCC_WARN_SHADOW = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ PREBINDING = NO;
+ SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "EchoServer" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C01FCF4B08A954540054247B /* Debug */,
+ C01FCF4C08A954540054247B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ C01FCF4E08A954540054247B /* Build configuration list for PBXProject "EchoServer" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C01FCF4F08A954540054247B /* Debug */,
+ C01FCF5008A954540054247B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
--- /dev/null
+//
+// Prefix header for all source files of the 'EchoServer' target in the 'EchoServer' project
+//
+
+#ifdef __OBJC__
+ #import <Cocoa/Cocoa.h>
+#endif
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.01">
+ <data>
+ <int key="IBDocument.SystemTarget">1050</int>
+ <string key="IBDocument.SystemVersion">9E17</string>
+ <string key="IBDocument.InterfaceBuilderVersion">629</string>
+ <string key="IBDocument.AppKitVersion">949.33</string>
+ <string key="IBDocument.HIToolboxVersion">352.00</string>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="371"/>
+ <integer value="29"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string id="617114957">com.apple.InterfaceBuilderKit</string>
+ <string id="178373603">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1021">
+ <string key="NSClassName" id="1039455869">NSApplication</string>
+ </object>
+ <object class="NSCustomObject" id="1014">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1050">
+ <reference key="NSClassName" ref="1039455869"/>
+ </object>
+ <object class="NSMenu" id="649796088">
+ <string key="NSTitle">AMainMenu</string>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="694149608">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle" id="544929600">NewApplication</string>
+ <string key="NSKeyEquiv" id="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <object class="NSCustomResource" key="NSOnImage" id="464403078">
+ <string key="NSClassName" id="508877766">NSImage</string>
+ <string key="NSResourceName">NSMenuCheckmark</string>
+ </object>
+ <object class="NSCustomResource" key="NSMixedImage" id="406196301">
+ <reference key="NSClassName" ref="508877766"/>
+ <string key="NSResourceName">NSMenuMixedState</string>
+ </object>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="110575045">
+ <reference key="NSTitle" ref="544929600"/>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="238522557">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">About NewApplication</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="304266470">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <reference key="NSTitle" ref="857918456"/>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="609285721">
+ <reference key="NSMenu" ref="110575045"/>
+ <string type="base64-UTF8" key="NSTitle">UHJlZmVyZW5jZXPigKY</string>
+ <string key="NSKeyEquiv">,</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="481834944">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <reference key="NSTitle" ref="857918456"/>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="1046388886">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle" id="487627014">Services</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="752062318">
+ <reference key="NSTitle" ref="487627014"/>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <string key="NSName">_NSServicesMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="646227648">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <reference key="NSTitle" ref="857918456"/>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="755159360">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Hide NewApplication</string>
+ <string key="NSKeyEquiv" id="712247123">h</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="342932134">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Hide Others</string>
+ <reference key="NSKeyEquiv" ref="712247123"/>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="908899353">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Show All</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="1056857174">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <reference key="NSTitle" ref="857918456"/>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="632727374">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Quit NewApplication</string>
+ <string key="NSKeyEquiv">q</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ </object>
+ <string key="NSName">_NSAppleMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="379814623">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle" id="589729360">File</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="720053764">
+ <reference key="NSTitle" ref="589729360"/>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="705341025">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">New</string>
+ <string key="NSKeyEquiv">n</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="722745758">
+ <reference key="NSMenu" ref="720053764"/>
+ <string type="base64-UTF8" key="NSTitle">T3BlbuKApg</string>
+ <string key="NSKeyEquiv">o</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="1025936716">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle" id="1022827674">Open Recent</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="1065607017">
+ <reference key="NSTitle" ref="1022827674"/>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="759406840">
+ <reference key="NSMenu" ref="1065607017"/>
+ <string key="NSTitle">Clear Menu</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ </object>
+ <string key="NSName">_NSRecentDocumentsMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="425164168">
+ <reference key="NSMenu" ref="720053764"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <reference key="NSTitle" ref="857918456"/>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="776162233">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Close</string>
+ <string key="NSKeyEquiv">w</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="1023925487">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Save</string>
+ <string key="NSKeyEquiv">s</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="117038363">
+ <reference key="NSMenu" ref="720053764"/>
+ <string type="base64-UTF8" key="NSTitle">U2F2ZSBBc+KApg</string>
+ <string key="NSKeyEquiv">S</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="579971712">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Revert to Saved</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="1010469920">
+ <reference key="NSMenu" ref="720053764"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <reference key="NSTitle" ref="857918456"/>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="294629803">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Page Setup...</string>
+ <string key="NSKeyEquiv">P</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <reference key="NSToolTip" ref="857918456"/>
+ </object>
+ <object class="NSMenuItem" id="49223823">
+ <reference key="NSMenu" ref="720053764"/>
+ <string type="base64-UTF8" key="NSTitle">UHJpbnTigKY</string>
+ <string key="NSKeyEquiv">p</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="952259628">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle" id="459142759">Edit</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="789758025">
+ <reference key="NSTitle" ref="459142759"/>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="1058277027">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Undo</string>
+ <string key="NSKeyEquiv">z</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="790794224">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Redo</string>
+ <string key="NSKeyEquiv">Z</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="1040322652">
+ <reference key="NSMenu" ref="789758025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <reference key="NSTitle" ref="857918456"/>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="296257095">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Cut</string>
+ <string key="NSKeyEquiv">x</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="860595796">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Copy</string>
+ <string key="NSKeyEquiv">c</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="29853731">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Paste</string>
+ <string key="NSKeyEquiv">v</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="437104165">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Delete</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="583158037">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Select All</string>
+ <string key="NSKeyEquiv">a</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="212016141">
+ <reference key="NSMenu" ref="789758025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <reference key="NSTitle" ref="857918456"/>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="892235320">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle" id="1047723501">Find</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="963351320">
+ <reference key="NSTitle" ref="1047723501"/>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="447796847">
+ <reference key="NSMenu" ref="963351320"/>
+ <string type="base64-UTF8" key="NSTitle">RmluZOKApg</string>
+ <string key="NSKeyEquiv" id="109030866">f</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="326711663">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Find Next</string>
+ <string key="NSKeyEquiv" id="591118048">g</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="270902937">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Find Previous</string>
+ <string key="NSKeyEquiv" id="660224505">G</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <int key="NSTag">3</int>
+ </object>
+ <object class="NSMenuItem" id="159080638">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Use Selection for Find</string>
+ <string key="NSKeyEquiv">e</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <int key="NSTag">7</int>
+ </object>
+ <object class="NSMenuItem" id="88285865">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Jump to Selection</string>
+ <string key="NSKeyEquiv">j</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="972420730">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle" id="601373482">Spelling and Grammar</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="769623530">
+ <reference key="NSTitle" ref="601373482"/>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="679648819">
+ <reference key="NSMenu" ref="769623530"/>
+ <string type="base64-UTF8" key="NSTitle">U2hvdyBTcGVsbGluZ+KApg</string>
+ <string key="NSKeyEquiv">:</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="96193923">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Spelling</string>
+ <string key="NSKeyEquiv">;</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="948374510">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Spelling While Typing</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="967646866">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Grammar With Spelling</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="507821607">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle" id="239487307">Substitutions</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="698887838">
+ <reference key="NSTitle" ref="239487307"/>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="605118523">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Copy/Paste</string>
+ <reference key="NSKeyEquiv" ref="109030866"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="197661976">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Quotes</string>
+ <reference key="NSKeyEquiv" ref="591118048"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="708854459">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Links</string>
+ <reference key="NSKeyEquiv" ref="660224505"/>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <int key="NSTag">3</int>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="676164635">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle" id="1050731385">Speech</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="785027613">
+ <reference key="NSTitle" ref="1050731385"/>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="731782645">
+ <reference key="NSMenu" ref="785027613"/>
+ <string key="NSTitle">Start Speaking</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="680220178">
+ <reference key="NSMenu" ref="785027613"/>
+ <string key="NSTitle">Stop Speaking</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="626404410">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle" id="708565849">Format</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="502084290">
+ <reference key="NSTitle" ref="708565849"/>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="519768076">
+ <reference key="NSMenu" ref="502084290"/>
+ <string key="NSTitle">Show Fonts</string>
+ <string key="NSKeyEquiv" id="1016092764">t</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="1028416764">
+ <reference key="NSMenu" ref="502084290"/>
+ <string key="NSTitle">Show Colors</string>
+ <string key="NSKeyEquiv">C</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="586577488">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle" id="180305338">View</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="466310130">
+ <reference key="NSTitle" ref="180305338"/>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="102151532">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Show Toolbar</string>
+ <reference key="NSKeyEquiv" ref="1016092764"/>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="237841660">
+ <reference key="NSMenu" ref="466310130"/>
+ <string type="base64-UTF8" key="NSTitle">Q3VzdG9taXplIFRvb2xiYXLigKY</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="713487014">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle" id="15506412">Window</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="835318025">
+ <reference key="NSTitle" ref="15506412"/>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="1011231497">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Minimize</string>
+ <string key="NSKeyEquiv">m</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="575023229">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Zoom</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="299356726">
+ <reference key="NSMenu" ref="835318025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <reference key="NSTitle" ref="857918456"/>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ <object class="NSMenuItem" id="625202149">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Bring All to Front</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ </object>
+ <string key="NSName">_NSWindowsMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="391199113">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle" id="519223894">Help</string>
+ <reference key="NSKeyEquiv" ref="857918456"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="374024848">
+ <reference key="NSTitle" ref="519223894"/>
+ <object class="NSMutableArray" key="NSMenuItems">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMenuItem" id="238773614">
+ <reference key="NSMenu" ref="374024848"/>
+ <string key="NSTitle">NewApplication Help</string>
+ <string key="NSKeyEquiv">?</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="464403078"/>
+ <reference key="NSMixedImage" ref="406196301"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <string key="NSName">_NSMainMenu</string>
+ </object>
+ <object class="NSWindowTemplate" id="972006081">
+ <int key="NSWindowStyleMask">15</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{335, 392}, {364, 358}}</string>
+ <int key="NSWTFlags">1946157056</int>
+ <reference key="NSWindowTitle" ref="15506412"/>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMinSize">{250, 150}</string>
+ <object class="NSView" key="NSWindowView" id="439893737">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSTextField" id="37798317">
+ <reference key="NSNextResponder" ref="439893737"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{60, 316}, {63, 22}}</string>
+ <reference key="NSSuperview" ref="439893737"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="109716074">
+ <int key="NSCellFlags">-1804468671</int>
+ <int key="NSCellFlags2">272630784</int>
+ <reference key="NSContents" ref="857918456"/>
+ <object class="NSFont" key="NSSupport" id="345453730">
+ <string key="NSName" id="27735384">LucidaGrande</string>
+ <double key="NSSize">1.300000e+01</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <string key="NSPlaceholderString">Any</string>
+ <reference key="NSControlView" ref="37798317"/>
+ <bool key="NSDrawsBackground">YES</bool>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName" id="479045549">System</string>
+ <string key="NSColorName">textBackgroundColor</string>
+ <object class="NSColor" key="NSColor" id="828979410">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <reference key="NSCatalogName" ref="479045549"/>
+ <string key="NSColorName">textColor</string>
+ <object class="NSColor" key="NSColor" id="518544926">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MAA</bytes>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSTextField" id="1048681524">
+ <reference key="NSNextResponder" ref="439893737"/>
+ <int key="NSvFlags">268</int>
+ <string key="NSFrame">{{17, 318}, {38, 17}}</string>
+ <reference key="NSSuperview" ref="439893737"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="251998944">
+ <int key="NSCellFlags">67239488</int>
+ <int key="NSCellFlags2">272630784</int>
+ <string key="NSContents">Port:</string>
+ <reference key="NSSupport" ref="345453730"/>
+ <reference key="NSControlView" ref="1048681524"/>
+ <object class="NSColor" key="NSBackgroundColor">
+ <int key="NSColorSpace">6</int>
+ <reference key="NSCatalogName" ref="479045549"/>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor" id="244308676">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2OQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor">
+ <int key="NSColorSpace">6</int>
+ <reference key="NSCatalogName" ref="479045549"/>
+ <string key="NSColorName">controlTextColor</string>
+ <reference key="NSColor" ref="518544926"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSButton" id="478395592">
+ <reference key="NSNextResponder" ref="439893737"/>
+ <int key="NSvFlags">265</int>
+ <string key="NSFrame">{{254, 310}, {96, 32}}</string>
+ <reference key="NSSuperview" ref="439893737"/>
+ <reference key="NSWindow"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="770729566">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Start</string>
+ <reference key="NSSupport" ref="345453730"/>
+ <reference key="NSControlView" ref="478395592"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <reference key="NSAlternateContents" ref="857918456"/>
+ <reference key="NSKeyEquivalent" ref="857918456"/>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSScrollView" id="808079749">
+ <reference key="NSNextResponder" ref="439893737"/>
+ <int key="NSvFlags">274</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSClipView" id="753933742">
+ <reference key="NSNextResponder" ref="808079749"/>
+ <int key="NSvFlags">2304</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSTextView" id="35061812">
+ <reference key="NSNextResponder" ref="753933742"/>
+ <int key="NSvFlags">10514</int>
+ <string key="NSFrameSize">{322, 120}</string>
+ <reference key="NSSuperview" ref="753933742"/>
+ <reference key="NSWindow"/>
+ <object class="NSTextContainer" key="NSTextContainer" id="943374405">
+ <object class="NSLayoutManager" key="NSLayoutManager">
+ <object class="NSTextStorage" key="NSTextStorage">
+ <object class="NSMutableString" key="NSString">
+ <characters key="NS.bytes">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum Et harumd und lookum like Greek to me, dereud facilis est er expedit distinct. Nam liber te conscient to factor tum poen legum odioque civiuda</characters>
+ </object>
+ <object class="NSMutableArray" key="NSAttributes">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSDictionary">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string id="545058709">NSFont</string>
+ <string id="759172114">NSParagraphStyle</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSFont">
+ <reference key="NSName" ref="27735384"/>
+ <double key="NSSize">1.000000e+01</double>
+ <int key="NSfFlags">2843</int>
+ </object>
+ <object class="NSParagraphStyle">
+ <int key="NSAlignment">3</int>
+ <object class="NSArray" key="NSTabStops">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSTextTab" id="391676181">
+ <double key="NSLocation">0.000000e+00</double>
+ </object>
+ <object class="NSTextTab" id="937054670">
+ <double key="NSLocation">5.600000e+01</double>
+ </object>
+ <object class="NSTextTab" id="579976407">
+ <double key="NSLocation">1.120000e+02</double>
+ </object>
+ <object class="NSTextTab" id="461447862">
+ <double key="NSLocation">1.680000e+02</double>
+ </object>
+ <object class="NSTextTab" id="374678093">
+ <double key="NSLocation">2.240000e+02</double>
+ </object>
+ <object class="NSTextTab" id="457774899">
+ <double key="NSLocation">2.800000e+02</double>
+ </object>
+ <object class="NSTextTab" id="301812150">
+ <double key="NSLocation">3.360000e+02</double>
+ </object>
+ <object class="NSTextTab" id="606981213">
+ <double key="NSLocation">3.920000e+02</double>
+ </object>
+ <object class="NSTextTab" id="370182881">
+ <double key="NSLocation">4.480000e+02</double>
+ </object>
+ <object class="NSTextTab" id="713445094">
+ <double key="NSLocation">5.040000e+02</double>
+ </object>
+ <object class="NSTextTab" id="332171008">
+ <double key="NSLocation">5.600000e+02</double>
+ </object>
+ <object class="NSTextTab" id="462725174">
+ <double key="NSLocation">6.160000e+02</double>
+ </object>
+ <object class="NSTextTab" id="240732">
+ <double key="NSLocation">6.720000e+02</double>
+ </object>
+ <object class="NSTextTab" id="867356602">
+ <double key="NSLocation">7.280000e+02</double>
+ </object>
+ <object class="NSTextTab" id="731175877">
+ <double key="NSLocation">7.840000e+02</double>
+ </object>
+ <object class="NSTextTab" id="707002120">
+ <double key="NSLocation">8.400000e+02</double>
+ </object>
+ <object class="NSTextTab" id="596993831">
+ <double key="NSLocation">8.960000e+02</double>
+ </object>
+ <object class="NSTextTab" id="268943605">
+ <double key="NSLocation">9.520000e+02</double>
+ </object>
+ <object class="NSTextTab" id="737954820">
+ <double key="NSLocation">1.008000e+03</double>
+ </object>
+ <object class="NSTextTab" id="542894483">
+ <double key="NSLocation">1.064000e+03</double>
+ </object>
+ <object class="NSTextTab" id="306740922">
+ <double key="NSLocation">1.120000e+03</double>
+ </object>
+ <object class="NSTextTab" id="590772929">
+ <double key="NSLocation">1.176000e+03</double>
+ </object>
+ <object class="NSTextTab" id="888347213">
+ <double key="NSLocation">1.232000e+03</double>
+ </object>
+ <object class="NSTextTab" id="281678623">
+ <double key="NSLocation">1.288000e+03</double>
+ </object>
+ <object class="NSTextTab" id="35574032">
+ <double key="NSLocation">1.344000e+03</double>
+ </object>
+ <object class="NSTextTab" id="292998982">
+ <double key="NSLocation">1.400000e+03</double>
+ </object>
+ <object class="NSTextTab" id="16014550">
+ <double key="NSLocation">1.456000e+03</double>
+ </object>
+ <object class="NSTextTab" id="789506774">
+ <double key="NSLocation">1.512000e+03</double>
+ </object>
+ <object class="NSTextTab" id="1049600189">
+ <double key="NSLocation">1.568000e+03</double>
+ </object>
+ <object class="NSTextTab" id="6320735">
+ <double key="NSLocation">1.624000e+03</double>
+ </object>
+ <object class="NSTextTab" id="528829228">
+ <double key="NSLocation">1.680000e+03</double>
+ </object>
+ <object class="NSTextTab" id="367532548">
+ <double key="NSLocation">1.736000e+03</double>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSDictionary">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="545058709"/>
+ <reference ref="759172114"/>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSFont">
+ <string key="NSName">LucidaGrande-Bold</string>
+ <double key="NSSize">1.000000e+01</double>
+ <int key="NSfFlags">16</int>
+ </object>
+ <object class="NSParagraphStyle">
+ <int key="NSAlignment">3</int>
+ <object class="NSArray" key="NSTabStops">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="391676181"/>
+ <reference ref="937054670"/>
+ <reference ref="579976407"/>
+ <reference ref="461447862"/>
+ <reference ref="374678093"/>
+ <reference ref="457774899"/>
+ <reference ref="301812150"/>
+ <reference ref="606981213"/>
+ <reference ref="370182881"/>
+ <reference ref="713445094"/>
+ <reference ref="332171008"/>
+ <reference ref="462725174"/>
+ <reference ref="240732"/>
+ <reference ref="867356602"/>
+ <reference ref="731175877"/>
+ <reference ref="707002120"/>
+ <reference ref="596993831"/>
+ <reference ref="268943605"/>
+ <reference ref="737954820"/>
+ <reference ref="542894483"/>
+ <reference ref="306740922"/>
+ <reference ref="590772929"/>
+ <reference ref="888347213"/>
+ <reference ref="281678623"/>
+ <reference ref="35574032"/>
+ <reference ref="292998982"/>
+ <reference ref="16014550"/>
+ <reference ref="789506774"/>
+ <reference ref="1049600189"/>
+ <reference ref="6320735"/>
+ <reference ref="528829228"/>
+ <reference ref="367532548"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableData" key="NSAttributeInfo">
+ <bytes key="NS.bytes">GQAEAQgADAEiAAcBhAQAA</bytes>
+ </object>
+ <nil key="NSDelegate"/>
+ </object>
+ <object class="NSMutableArray" key="NSTextContainers">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="943374405"/>
+ </object>
+ <int key="NSLMFlags">6</int>
+ <nil key="NSDelegate"/>
+ </object>
+ <reference key="NSTextView" ref="35061812"/>
+ <double key="NSWidth">3.220000e+02</double>
+ <int key="NSTCFlags">1</int>
+ </object>
+ <object class="NSTextViewSharedData" key="NSSharedData">
+ <int key="NSFlags">11109</int>
+ <reference key="NSBackgroundColor" ref="828979410"/>
+ <reference key="NSInsertionColor" ref="518544926"/>
+ <object class="NSDictionary" key="NSSelectedAttributes">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>NSBackgroundColor</string>
+ <string id="492835712">NSColor</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSColor">
+ <int key="NSColorSpace">6</int>
+ <reference key="NSCatalogName" ref="479045549"/>
+ <string key="NSColorName">selectedTextBackgroundColor</string>
+ <reference key="NSColor" ref="244308676"/>
+ </object>
+ <object class="NSColor">
+ <int key="NSColorSpace">6</int>
+ <reference key="NSCatalogName" ref="479045549"/>
+ <string key="NSColorName">selectedTextColor</string>
+ <reference key="NSColor" ref="518544926"/>
+ </object>
+ </object>
+ </object>
+ <nil key="NSMarkedAttributes"/>
+ <object class="NSDictionary" key="NSLinkAttributes">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="492835712"/>
+ <string>NSUnderline</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSColor">
+ <int key="NSColorSpace">1</int>
+ <bytes key="NSRGB">MCAwIDEAA</bytes>
+ </object>
+ <integer value="1" id="9"/>
+ </object>
+ </object>
+ <nil key="NSDefaultParagraphStyle"/>
+ </object>
+ <int key="NSTVFlags">6</int>
+ <string key="NSMaxSize">{454, 1e+07}</string>
+ <string key="NSMinize">{84, 0}</string>
+ <nil key="NSDelegate"/>
+ </object>
+ </object>
+ <string key="NSFrame">{{1, 1}, {322, 286}}</string>
+ <reference key="NSSuperview" ref="808079749"/>
+ <reference key="NSWindow"/>
+ <reference key="NSNextKeyView" ref="35061812"/>
+ <reference key="NSDocView" ref="35061812"/>
+ <reference key="NSBGColor" ref="828979410"/>
+ <object class="NSCursor" key="NSCursor">
+ <string key="NSHotSpot">{4, -5}</string>
+ <int key="NSCursorType">1</int>
+ </object>
+ <int key="NScvFlags">4</int>
+ </object>
+ <object class="NSScroller" id="851288889">
+ <reference key="NSNextResponder" ref="808079749"/>
+ <int key="NSvFlags">-2147483392</int>
+ <string key="NSFrame">{{308, 1}, {15, 286}}</string>
+ <reference key="NSSuperview" ref="808079749"/>
+ <reference key="NSWindow"/>
+ <reference key="NSTarget" ref="808079749"/>
+ <string key="NSAction" id="57047737">_doScroller:</string>
+ <double key="NSPercent">3.579474e-01</double>
+ </object>
+ <object class="NSScroller" id="863017593">
+ <reference key="NSNextResponder" ref="808079749"/>
+ <int key="NSvFlags">256</int>
+ <string key="NSFrame">{{-100, -100}, {87, 18}}</string>
+ <reference key="NSSuperview" ref="808079749"/>
+ <reference key="NSWindow"/>
+ <int key="NSsFlags">1</int>
+ <reference key="NSTarget" ref="808079749"/>
+ <reference key="NSAction" ref="57047737"/>
+ <double key="NSCurValue">1.000000e+00</double>
+ <double key="NSPercent">9.456522e-01</double>
+ </object>
+ </object>
+ <string key="NSFrame">{{20, 20}, {324, 288}}</string>
+ <reference key="NSSuperview" ref="439893737"/>
+ <reference key="NSWindow"/>
+ <reference key="NSNextKeyView" ref="753933742"/>
+ <int key="NSsFlags">530</int>
+ <reference key="NSVScroller" ref="851288889"/>
+ <reference key="NSHScroller" ref="863017593"/>
+ <reference key="NSContentView" ref="753933742"/>
+ </object>
+ </object>
+ <string key="NSFrameSize">{364, 358}</string>
+ <reference key="NSSuperview"/>
+ <reference key="NSWindow"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
+ <string key="NSMinSize">{250, 172}</string>
+ <string key="NSFrameAutosaveName">EchoServerWindow</string>
+ </object>
+ <object class="NSCustomObject" id="977069679">
+ <string key="NSClassName" id="107999311">AppController</string>
+ </object>
+ <object class="NSUserDefaultsController" id="563223037">
+ <bool key="NSSharedInstance">YES</bool>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performMiniaturize:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1011231497"/>
+ </object>
+ <int key="connectionID">37</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">arrangeInFront:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="625202149"/>
+ </object>
+ <int key="connectionID">39</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">print:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="49223823"/>
+ </object>
+ <int key="connectionID">86</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">runPageLayout:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="294629803"/>
+ </object>
+ <int key="connectionID">87</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">clearRecentDocuments:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="759406840"/>
+ </object>
+ <int key="connectionID">127</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontStandardAboutPanel:</string>
+ <reference key="source" ref="1021"/>
+ <reference key="destination" ref="238522557"/>
+ </object>
+ <int key="connectionID">142</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performClose:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="776162233"/>
+ </object>
+ <int key="connectionID">193</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleContinuousSpellChecking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="948374510"/>
+ </object>
+ <int key="connectionID">222</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">undo:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1058277027"/>
+ </object>
+ <int key="connectionID">223</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">copy:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="860595796"/>
+ </object>
+ <int key="connectionID">224</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">checkSpelling:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="96193923"/>
+ </object>
+ <int key="connectionID">225</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">paste:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="29853731"/>
+ </object>
+ <int key="connectionID">226</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">stopSpeaking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="680220178"/>
+ </object>
+ <int key="connectionID">227</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">cut:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="296257095"/>
+ </object>
+ <int key="connectionID">228</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showGuessPanel:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="679648819"/>
+ </object>
+ <int key="connectionID">230</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">redo:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="790794224"/>
+ </object>
+ <int key="connectionID">231</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">selectAll:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="583158037"/>
+ </object>
+ <int key="connectionID">232</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">startSpeaking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="731782645"/>
+ </object>
+ <int key="connectionID">233</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">delete:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="437104165"/>
+ </object>
+ <int key="connectionID">235</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performZoom:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="575023229"/>
+ </object>
+ <int key="connectionID">240</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performFindPanelAction:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="447796847"/>
+ </object>
+ <int key="connectionID">241</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">centerSelectionInVisibleArea:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="88285865"/>
+ </object>
+ <int key="connectionID">245</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleGrammarChecking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="967646866"/>
+ </object>
+ <int key="connectionID">347</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleSmartInsertDelete:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="605118523"/>
+ </object>
+ <int key="connectionID">355</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticQuoteSubstitution:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="197661976"/>
+ </object>
+ <int key="connectionID">356</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticLinkDetection:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="708854459"/>
+ </object>
+ <int key="connectionID">357</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showHelp:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="238773614"/>
+ </object>
+ <int key="connectionID">360</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontColorPanel:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1028416764"/>
+ </object>
+ <int key="connectionID">361</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">saveDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1023925487"/>
+ </object>
+ <int key="connectionID">362</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">saveDocumentAs:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="117038363"/>
+ </object>
+ <int key="connectionID">363</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">revertDocumentToSaved:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="579971712"/>
+ </object>
+ <int key="connectionID">364</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">runToolbarCustomizationPalette:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="237841660"/>
+ </object>
+ <int key="connectionID">365</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleToolbarShown:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="102151532"/>
+ </object>
+ <int key="connectionID">366</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">hide:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="755159360"/>
+ </object>
+ <int key="connectionID">367</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">hideOtherApplications:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="342932134"/>
+ </object>
+ <int key="connectionID">368</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">terminate:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="632727374"/>
+ </object>
+ <int key="connectionID">369</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">unhideAllApplications:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="908899353"/>
+ </object>
+ <int key="connectionID">370</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">newDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="705341025"/>
+ </object>
+ <int key="connectionID">373</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">openDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="722745758"/>
+ </object>
+ <int key="connectionID">374</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="1050"/>
+ <reference key="destination" ref="977069679"/>
+ </object>
+ <int key="connectionID">386</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">logView</string>
+ <reference key="source" ref="977069679"/>
+ <reference key="destination" ref="35061812"/>
+ </object>
+ <int key="connectionID">387</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">portField</string>
+ <reference key="source" ref="977069679"/>
+ <reference key="destination" ref="37798317"/>
+ </object>
+ <int key="connectionID">388</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">startStopButton</string>
+ <reference key="source" ref="977069679"/>
+ <reference key="destination" ref="478395592"/>
+ </object>
+ <int key="connectionID">389</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">startStop:</string>
+ <reference key="source" ref="977069679"/>
+ <reference key="destination" ref="478395592"/>
+ </object>
+ <int key="connectionID">390</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label" id="936887969">value: values.serverPort</string>
+ <reference key="source" ref="37798317"/>
+ <reference key="destination" ref="563223037"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="37798317"/>
+ <reference key="NSDestination" ref="563223037"/>
+ <reference key="NSLabel" ref="936887969"/>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">values.serverPort</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">392</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">initialFirstResponder</string>
+ <reference key="source" ref="972006081"/>
+ <reference key="destination" ref="478395592"/>
+ </object>
+ <int key="connectionID">393</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="1049">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1048"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1021"/>
+ <reference key="parent" ref="1049"/>
+ <string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1014"/>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1050"/>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">29</int>
+ <reference key="object" ref="649796088"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="713487014"/>
+ <reference ref="694149608"/>
+ <reference ref="391199113"/>
+ <reference ref="952259628"/>
+ <reference ref="379814623"/>
+ <reference ref="586577488"/>
+ <reference ref="626404410"/>
+ </object>
+ <reference key="parent" ref="1049"/>
+ <string key="objectName">MainMenu</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">19</int>
+ <reference key="object" ref="713487014"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="835318025"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">56</int>
+ <reference key="object" ref="694149608"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="110575045"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">103</int>
+ <reference key="object" ref="391199113"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="374024848"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ <string key="objectName" id="344781584">1</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">217</int>
+ <reference key="object" ref="952259628"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="789758025"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">83</int>
+ <reference key="object" ref="379814623"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="720053764"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">81</int>
+ <reference key="object" ref="720053764"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1023925487"/>
+ <reference ref="117038363"/>
+ <reference ref="49223823"/>
+ <reference ref="722745758"/>
+ <reference ref="705341025"/>
+ <reference ref="1025936716"/>
+ <reference ref="294629803"/>
+ <reference ref="776162233"/>
+ <reference ref="425164168"/>
+ <reference ref="579971712"/>
+ <reference ref="1010469920"/>
+ </object>
+ <reference key="parent" ref="379814623"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">75</int>
+ <reference key="object" ref="1023925487"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">3</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">80</int>
+ <reference key="object" ref="117038363"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">8</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">78</int>
+ <reference key="object" ref="49223823"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">6</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">72</int>
+ <reference key="object" ref="722745758"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">82</int>
+ <reference key="object" ref="705341025"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">9</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">124</int>
+ <reference key="object" ref="1025936716"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1065607017"/>
+ </object>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">77</int>
+ <reference key="object" ref="294629803"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">5</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">73</int>
+ <reference key="object" ref="776162233"/>
+ <reference key="parent" ref="720053764"/>
+ <reference key="objectName" ref="344781584"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">79</int>
+ <reference key="object" ref="425164168"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">7</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">112</int>
+ <reference key="object" ref="579971712"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName">10</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">74</int>
+ <reference key="object" ref="1010469920"/>
+ <reference key="parent" ref="720053764"/>
+ <string key="objectName" id="5220549">2</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">125</int>
+ <reference key="object" ref="1065607017"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="759406840"/>
+ </object>
+ <reference key="parent" ref="1025936716"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">126</int>
+ <reference key="object" ref="759406840"/>
+ <reference key="parent" ref="1065607017"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">205</int>
+ <reference key="object" ref="789758025"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="437104165"/>
+ <reference ref="583158037"/>
+ <reference ref="1058277027"/>
+ <reference ref="212016141"/>
+ <reference ref="296257095"/>
+ <reference ref="29853731"/>
+ <reference ref="860595796"/>
+ <reference ref="1040322652"/>
+ <reference ref="790794224"/>
+ <reference ref="892235320"/>
+ <reference ref="972420730"/>
+ <reference ref="676164635"/>
+ <reference ref="507821607"/>
+ </object>
+ <reference key="parent" ref="952259628"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">202</int>
+ <reference key="object" ref="437104165"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">198</int>
+ <reference key="object" ref="583158037"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">207</int>
+ <reference key="object" ref="1058277027"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">214</int>
+ <reference key="object" ref="212016141"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">199</int>
+ <reference key="object" ref="296257095"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">203</int>
+ <reference key="object" ref="29853731"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">197</int>
+ <reference key="object" ref="860595796"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">206</int>
+ <reference key="object" ref="1040322652"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">215</int>
+ <reference key="object" ref="790794224"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">218</int>
+ <reference key="object" ref="892235320"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="963351320"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">216</int>
+ <reference key="object" ref="972420730"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="769623530"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">200</int>
+ <reference key="object" ref="769623530"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="948374510"/>
+ <reference ref="96193923"/>
+ <reference ref="679648819"/>
+ <reference ref="967646866"/>
+ </object>
+ <reference key="parent" ref="972420730"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">219</int>
+ <reference key="object" ref="948374510"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">201</int>
+ <reference key="object" ref="96193923"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">204</int>
+ <reference key="object" ref="679648819"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">220</int>
+ <reference key="object" ref="963351320"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="270902937"/>
+ <reference ref="88285865"/>
+ <reference ref="159080638"/>
+ <reference ref="326711663"/>
+ <reference ref="447796847"/>
+ </object>
+ <reference key="parent" ref="892235320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">213</int>
+ <reference key="object" ref="270902937"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">210</int>
+ <reference key="object" ref="88285865"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">221</int>
+ <reference key="object" ref="159080638"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">208</int>
+ <reference key="object" ref="326711663"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">209</int>
+ <reference key="object" ref="447796847"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">106</int>
+ <reference key="object" ref="374024848"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="238773614"/>
+ </object>
+ <reference key="parent" ref="391199113"/>
+ <reference key="objectName" ref="5220549"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">111</int>
+ <reference key="object" ref="238773614"/>
+ <reference key="parent" ref="374024848"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">57</int>
+ <reference key="object" ref="110575045"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="238522557"/>
+ <reference ref="755159360"/>
+ <reference ref="908899353"/>
+ <reference ref="632727374"/>
+ <reference ref="646227648"/>
+ <reference ref="609285721"/>
+ <reference ref="481834944"/>
+ <reference ref="304266470"/>
+ <reference ref="1046388886"/>
+ <reference ref="1056857174"/>
+ <reference ref="342932134"/>
+ </object>
+ <reference key="parent" ref="694149608"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">58</int>
+ <reference key="object" ref="238522557"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">134</int>
+ <reference key="object" ref="755159360"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">150</int>
+ <reference key="object" ref="908899353"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">136</int>
+ <reference key="object" ref="632727374"/>
+ <reference key="parent" ref="110575045"/>
+ <string key="objectName">1111</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">144</int>
+ <reference key="object" ref="646227648"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">129</int>
+ <reference key="object" ref="609285721"/>
+ <reference key="parent" ref="110575045"/>
+ <string key="objectName">121</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">143</int>
+ <reference key="object" ref="481834944"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">236</int>
+ <reference key="object" ref="304266470"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">131</int>
+ <reference key="object" ref="1046388886"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="752062318"/>
+ </object>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">149</int>
+ <reference key="object" ref="1056857174"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">145</int>
+ <reference key="object" ref="342932134"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">130</int>
+ <reference key="object" ref="752062318"/>
+ <reference key="parent" ref="1046388886"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">24</int>
+ <reference key="object" ref="835318025"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="299356726"/>
+ <reference ref="625202149"/>
+ <reference ref="575023229"/>
+ <reference ref="1011231497"/>
+ </object>
+ <reference key="parent" ref="713487014"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">92</int>
+ <reference key="object" ref="299356726"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5</int>
+ <reference key="object" ref="625202149"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">239</int>
+ <reference key="object" ref="575023229"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">23</int>
+ <reference key="object" ref="1011231497"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">295</int>
+ <reference key="object" ref="586577488"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="466310130"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">296</int>
+ <reference key="object" ref="466310130"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="102151532"/>
+ <reference ref="237841660"/>
+ </object>
+ <reference key="parent" ref="586577488"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">297</int>
+ <reference key="object" ref="102151532"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">298</int>
+ <reference key="object" ref="237841660"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">299</int>
+ <reference key="object" ref="626404410"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="502084290"/>
+ </object>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">300</int>
+ <reference key="object" ref="502084290"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="519768076"/>
+ <reference ref="1028416764"/>
+ </object>
+ <reference key="parent" ref="626404410"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">344</int>
+ <reference key="object" ref="519768076"/>
+ <reference key="parent" ref="502084290"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">345</int>
+ <reference key="object" ref="1028416764"/>
+ <reference key="parent" ref="502084290"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">211</int>
+ <reference key="object" ref="676164635"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="785027613"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">212</int>
+ <reference key="object" ref="785027613"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="680220178"/>
+ <reference ref="731782645"/>
+ </object>
+ <reference key="parent" ref="676164635"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">195</int>
+ <reference key="object" ref="680220178"/>
+ <reference key="parent" ref="785027613"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">196</int>
+ <reference key="object" ref="731782645"/>
+ <reference key="parent" ref="785027613"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">346</int>
+ <reference key="object" ref="967646866"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">348</int>
+ <reference key="object" ref="507821607"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="698887838"/>
+ </object>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">349</int>
+ <reference key="object" ref="698887838"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="605118523"/>
+ <reference ref="197661976"/>
+ <reference ref="708854459"/>
+ </object>
+ <reference key="parent" ref="507821607"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">350</int>
+ <reference key="object" ref="605118523"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">351</int>
+ <reference key="object" ref="197661976"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">354</int>
+ <reference key="object" ref="708854459"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">371</int>
+ <reference key="object" ref="972006081"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="439893737"/>
+ </object>
+ <reference key="parent" ref="1049"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">372</int>
+ <reference key="object" ref="439893737"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="37798317"/>
+ <reference ref="1048681524"/>
+ <reference ref="478395592"/>
+ <reference ref="808079749"/>
+ </object>
+ <reference key="parent" ref="972006081"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">375</int>
+ <reference key="object" ref="37798317"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="109716074"/>
+ </object>
+ <reference key="parent" ref="439893737"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">376</int>
+ <reference key="object" ref="109716074"/>
+ <reference key="parent" ref="37798317"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">377</int>
+ <reference key="object" ref="1048681524"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="251998944"/>
+ </object>
+ <reference key="parent" ref="439893737"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">378</int>
+ <reference key="object" ref="251998944"/>
+ <reference key="parent" ref="1048681524"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">379</int>
+ <reference key="object" ref="478395592"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="770729566"/>
+ </object>
+ <reference key="parent" ref="439893737"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">380</int>
+ <reference key="object" ref="770729566"/>
+ <reference key="parent" ref="478395592"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">381</int>
+ <reference key="object" ref="977069679"/>
+ <reference key="parent" ref="1049"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">382</int>
+ <reference key="object" ref="808079749"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="851288889"/>
+ <reference ref="863017593"/>
+ <reference ref="35061812"/>
+ </object>
+ <reference key="parent" ref="439893737"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">383</int>
+ <reference key="object" ref="851288889"/>
+ <reference key="parent" ref="808079749"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">384</int>
+ <reference key="object" ref="863017593"/>
+ <reference key="parent" ref="808079749"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">385</int>
+ <reference key="object" ref="35061812"/>
+ <reference key="parent" ref="808079749"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">391</int>
+ <reference key="object" ref="563223037"/>
+ <reference key="parent" ref="1049"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-1.IBPluginDependency</string>
+ <string>-2.IBPluginDependency</string>
+ <string>-3.IBPluginDependency</string>
+ <string>103.IBPluginDependency</string>
+ <string>103.ImportedFromIB2</string>
+ <string>106.IBPluginDependency</string>
+ <string>106.ImportedFromIB2</string>
+ <string>106.editorWindowContentRectSynchronizationRect</string>
+ <string>111.IBPluginDependency</string>
+ <string>111.ImportedFromIB2</string>
+ <string>112.IBPluginDependency</string>
+ <string>112.ImportedFromIB2</string>
+ <string>124.IBPluginDependency</string>
+ <string>124.ImportedFromIB2</string>
+ <string>125.IBPluginDependency</string>
+ <string>125.ImportedFromIB2</string>
+ <string>125.editorWindowContentRectSynchronizationRect</string>
+ <string>126.IBPluginDependency</string>
+ <string>126.ImportedFromIB2</string>
+ <string>129.IBPluginDependency</string>
+ <string>129.ImportedFromIB2</string>
+ <string>130.IBPluginDependency</string>
+ <string>130.ImportedFromIB2</string>
+ <string>130.editorWindowContentRectSynchronizationRect</string>
+ <string>131.IBPluginDependency</string>
+ <string>131.ImportedFromIB2</string>
+ <string>134.IBPluginDependency</string>
+ <string>134.ImportedFromIB2</string>
+ <string>136.IBPluginDependency</string>
+ <string>136.ImportedFromIB2</string>
+ <string>143.IBPluginDependency</string>
+ <string>143.ImportedFromIB2</string>
+ <string>144.IBPluginDependency</string>
+ <string>144.ImportedFromIB2</string>
+ <string>145.IBPluginDependency</string>
+ <string>145.ImportedFromIB2</string>
+ <string>149.IBPluginDependency</string>
+ <string>149.ImportedFromIB2</string>
+ <string>150.IBPluginDependency</string>
+ <string>150.ImportedFromIB2</string>
+ <string>19.IBPluginDependency</string>
+ <string>19.ImportedFromIB2</string>
+ <string>195.IBPluginDependency</string>
+ <string>195.ImportedFromIB2</string>
+ <string>196.IBPluginDependency</string>
+ <string>196.ImportedFromIB2</string>
+ <string>197.IBPluginDependency</string>
+ <string>197.ImportedFromIB2</string>
+ <string>198.IBPluginDependency</string>
+ <string>198.ImportedFromIB2</string>
+ <string>199.IBPluginDependency</string>
+ <string>199.ImportedFromIB2</string>
+ <string>200.IBPluginDependency</string>
+ <string>200.ImportedFromIB2</string>
+ <string>200.editorWindowContentRectSynchronizationRect</string>
+ <string>201.IBPluginDependency</string>
+ <string>201.ImportedFromIB2</string>
+ <string>202.IBPluginDependency</string>
+ <string>202.ImportedFromIB2</string>
+ <string>203.IBPluginDependency</string>
+ <string>203.ImportedFromIB2</string>
+ <string>204.IBPluginDependency</string>
+ <string>204.ImportedFromIB2</string>
+ <string>205.IBPluginDependency</string>
+ <string>205.ImportedFromIB2</string>
+ <string>205.editorWindowContentRectSynchronizationRect</string>
+ <string>206.IBPluginDependency</string>
+ <string>206.ImportedFromIB2</string>
+ <string>207.IBPluginDependency</string>
+ <string>207.ImportedFromIB2</string>
+ <string>208.IBPluginDependency</string>
+ <string>208.ImportedFromIB2</string>
+ <string>209.IBPluginDependency</string>
+ <string>209.ImportedFromIB2</string>
+ <string>210.IBPluginDependency</string>
+ <string>210.ImportedFromIB2</string>
+ <string>211.IBPluginDependency</string>
+ <string>211.ImportedFromIB2</string>
+ <string>212.IBPluginDependency</string>
+ <string>212.ImportedFromIB2</string>
+ <string>212.editorWindowContentRectSynchronizationRect</string>
+ <string>213.IBPluginDependency</string>
+ <string>213.ImportedFromIB2</string>
+ <string>214.IBPluginDependency</string>
+ <string>214.ImportedFromIB2</string>
+ <string>215.IBPluginDependency</string>
+ <string>215.ImportedFromIB2</string>
+ <string>216.IBPluginDependency</string>
+ <string>216.ImportedFromIB2</string>
+ <string>217.IBPluginDependency</string>
+ <string>217.ImportedFromIB2</string>
+ <string>218.IBPluginDependency</string>
+ <string>218.ImportedFromIB2</string>
+ <string>219.IBPluginDependency</string>
+ <string>219.ImportedFromIB2</string>
+ <string>220.IBPluginDependency</string>
+ <string>220.ImportedFromIB2</string>
+ <string>220.editorWindowContentRectSynchronizationRect</string>
+ <string>221.IBPluginDependency</string>
+ <string>221.ImportedFromIB2</string>
+ <string>23.IBPluginDependency</string>
+ <string>23.ImportedFromIB2</string>
+ <string>236.IBPluginDependency</string>
+ <string>236.ImportedFromIB2</string>
+ <string>239.IBPluginDependency</string>
+ <string>239.ImportedFromIB2</string>
+ <string>24.IBPluginDependency</string>
+ <string>24.ImportedFromIB2</string>
+ <string>24.editorWindowContentRectSynchronizationRect</string>
+ <string>29.IBPluginDependency</string>
+ <string>29.ImportedFromIB2</string>
+ <string>29.WindowOrigin</string>
+ <string>29.editorWindowContentRectSynchronizationRect</string>
+ <string>295.IBPluginDependency</string>
+ <string>296.IBPluginDependency</string>
+ <string>296.editorWindowContentRectSynchronizationRect</string>
+ <string>297.IBPluginDependency</string>
+ <string>298.IBPluginDependency</string>
+ <string>299.IBPluginDependency</string>
+ <string>300.IBPluginDependency</string>
+ <string>300.editorWindowContentRectSynchronizationRect</string>
+ <string>344.IBPluginDependency</string>
+ <string>345.IBPluginDependency</string>
+ <string>346.IBPluginDependency</string>
+ <string>346.ImportedFromIB2</string>
+ <string>348.IBPluginDependency</string>
+ <string>348.ImportedFromIB2</string>
+ <string>349.IBPluginDependency</string>
+ <string>349.ImportedFromIB2</string>
+ <string>349.editorWindowContentRectSynchronizationRect</string>
+ <string>350.IBPluginDependency</string>
+ <string>350.ImportedFromIB2</string>
+ <string>351.IBPluginDependency</string>
+ <string>351.ImportedFromIB2</string>
+ <string>354.IBPluginDependency</string>
+ <string>354.ImportedFromIB2</string>
+ <string>371.IBPluginDependency</string>
+ <string>371.IBWindowTemplateEditedContentRect</string>
+ <string>371.NSWindowTemplate.visibleAtLaunch</string>
+ <string>371.editorWindowContentRectSynchronizationRect</string>
+ <string>371.windowTemplate.hasMinSize</string>
+ <string>371.windowTemplate.minSize</string>
+ <string>372.IBPluginDependency</string>
+ <string>375.IBPluginDependency</string>
+ <string>376.IBPluginDependency</string>
+ <string>377.IBPluginDependency</string>
+ <string>378.IBPluginDependency</string>
+ <string>379.IBPluginDependency</string>
+ <string>380.IBPluginDependency</string>
+ <string>381.IBPluginDependency</string>
+ <string>382.IBPluginDependency</string>
+ <string>383.IBPluginDependency</string>
+ <string>384.IBPluginDependency</string>
+ <string>385.IBPluginDependency</string>
+ <string>5.IBPluginDependency</string>
+ <string>5.ImportedFromIB2</string>
+ <string>56.IBPluginDependency</string>
+ <string>56.ImportedFromIB2</string>
+ <string>57.IBPluginDependency</string>
+ <string>57.ImportedFromIB2</string>
+ <string>57.editorWindowContentRectSynchronizationRect</string>
+ <string>58.IBPluginDependency</string>
+ <string>58.ImportedFromIB2</string>
+ <string>72.IBPluginDependency</string>
+ <string>72.ImportedFromIB2</string>
+ <string>73.IBPluginDependency</string>
+ <string>73.ImportedFromIB2</string>
+ <string>74.IBPluginDependency</string>
+ <string>74.ImportedFromIB2</string>
+ <string>75.IBPluginDependency</string>
+ <string>75.ImportedFromIB2</string>
+ <string>77.IBPluginDependency</string>
+ <string>77.ImportedFromIB2</string>
+ <string>78.IBPluginDependency</string>
+ <string>78.ImportedFromIB2</string>
+ <string>79.IBPluginDependency</string>
+ <string>79.ImportedFromIB2</string>
+ <string>80.IBPluginDependency</string>
+ <string>80.ImportedFromIB2</string>
+ <string>81.IBPluginDependency</string>
+ <string>81.ImportedFromIB2</string>
+ <string>81.editorWindowContentRectSynchronizationRect</string>
+ <string>82.IBPluginDependency</string>
+ <string>82.ImportedFromIB2</string>
+ <string>83.IBPluginDependency</string>
+ <string>83.ImportedFromIB2</string>
+ <string>92.IBPluginDependency</string>
+ <string>92.ImportedFromIB2</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="178373603"/>
+ <reference ref="617114957"/>
+ <reference ref="617114957"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <string>{{596, 852}, {216, 23}}</string>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <string>{{522, 812}, {146, 23}}</string>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <string>{{436, 809}, {64, 6}}</string>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <string>{{608, 612}, {275, 83}}</string>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <string>{{187, 434}, {243, 243}}</string>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <string>{{608, 612}, {167, 43}}</string>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <string>{{608, 612}, {241, 103}}</string>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <string>{{525, 802}, {197, 73}}</string>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <string>{74, 862}</string>
+ <string>{{106, 958}, {478, 20}}</string>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <string>{{475, 832}, {234, 43}}</string>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <string>{{231, 634}, {176, 43}}</string>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <string>{{608, 612}, {215, 63}}</string>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <string id="566812099">{{108, 527}, {364, 358}}</string>
+ <reference ref="9"/>
+ <reference ref="566812099"/>
+ <integer value="1"/>
+ <string>{250, 150}</string>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <string>{{23, 794}, {245, 183}}</string>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <string>{{145, 474}, {199, 203}}</string>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ <reference ref="178373603"/>
+ <reference ref="9"/>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">393</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className" id="867902027">NSObject</string>
+ <nil key="superclassName"/>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey" id="1044827585">IBProjectSource</string>
+ <string key="minorKey">AsyncSocket.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">AppController</string>
+ <reference key="superclassName" ref="867902027"/>
+ <object class="NSMutableDictionary" key="actions">
+ <string key="NS.key.0">startStop:</string>
+ <string key="NS.object.0" id="299522822">id</string>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>logView</string>
+ <string>portField</string>
+ <string>startStopButton</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="299522822"/>
+ <reference ref="299522822"/>
+ <reference ref="299522822"/>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <reference key="majorKey" ref="1044827585"/>
+ <string key="minorKey">AppController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <reference key="className" ref="107999311"/>
+ <nil key="superclassName"/>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBUserSource</string>
+ <reference key="minorKey" ref="857918456"/>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../../EchoServer.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ <object class="NSMutableData" key="IBDocument.RunnableNib">
+ <bytes key="NS.bytes">YnBsaXN0MDDUAAEAAgADAAQABQAGAAkAClgkdmVyc2lvblQkdG9wWSRhcmNoaXZlclgkb2JqZWN0cxIA
+AYag0QAHAAhdSUIub2JqZWN0ZGF0YYABXxAPTlNLZXllZEFyY2hpdmVyrxECxAALAAwAMQA1ADYAPAA9
+AEIAXABdAF4AXwBgAAsAbQB0AIAAgQCVAJYAngCfAKIAowCtAK4ArwC0ALYAuwC8AL8AwwDJANEA0gDa
+ANsA4ADhAOQA6QDqAPMA9AEHAQgBCwEOASABJgE3ATsBTQFOAVgBYAFpAW0BcAF1AX4AoQF/AYQBpwGr
+Aa4BsQG0AbcBugG9AcABwwHGAckBzAHPAdIB1QHYAdsB3gHhAeQB5wHqAe0B8AHzAfYB+QH8Af8CAgIF
+AggCCwIOAhACFQIYAiACIwJGAkoCSwJOAlICVgJaAl4CYAJiAnEAgwCkAnkCfgJ/AoQChQKNAVMCjgKS
+ApUClgKXApoCmwKgAqECowKmArMCtAK1ArgCwwLEAsUCyAALAskCywLMAs0CzgLRAtUDBgMMAx0DIgMj
+AyQDKQMqAysDLgMyAzMDNgM3AzsDPwNGA0oDSwNMA00DUQNYA10DXgNfA2MDawNsA20DbgNyA3kDfQN+
+A38DgAOEA4sDkAORA5IDlgOfA6ADoQOiA6YDrQOxA7IDswO0A7gDvwPDA8QDxQPJA9AD0QPSA9MD1wPe
+A98D4APkA+sD7APtA/MD+gP7A/wEAAQHBAsEDAQNBBEEGQQaBBsEHAQgBCcELAQtBC4EMgQ5BDoEOwQ8
+BEAERwRIBEkETQRUBFgEWQRaBFsEXwRmBGoEawRsBG0EcQR4BHkEegR7BH8EhgSHBIgEiQSNBJQElQSW
+BJcEmwSiBKMEpASlBKkEsASxBLIEswS3BL4EvwTABMQEywTMBM0EzgTSBNkE2gTbBNwE4ATnBOgE6QTu
+BO8E8gT2BP0FAQUCBQMFBAUJBQwFDQUOBRIFGQUaBRsFHwUmBScFKAUpBS4FLwUzBToFOwU8BT0FQQVI
+BUkFSgVLBVAFUQVVBVwFXQVeBWMFZgVnBWsFcgVzBXQFeAV/BYAFgQWCBYcFiAWMBZMFlAWVBZYFmgWh
+BaIFowWkBa4FsgW2BbcFuAW5BbwGHgYoBi0GLgYvBj0GSAZPBlAGUQZaBmMGLgZkBmkGbAZtBm8GeAaB
+BoIGLgaLBpIGmwacBp0GpQasBq0GtAa1Br0Gvga/BsAGxQbOBi4GzwbUBtsG3AblBu4GLgbvBvQG9Qb5
+BvoG/gb/BwgHEQYuBxIHIAYuBykHMgczB0MHTAYuB1UHXgYuB18HZgdvBi4HcAd2Bi4HfwYuB4gGLgeR
+B5IHnAedB/8IYgjFCMYIxwjICMkIygjLCMwIzQjOCM8I0AjRCNII0wjUCNUI1gjXCNgI2QjaCNsI3Ajd
+CN4I3wjgCOEI4gjjCOQI5QjmCOcI6AjpCOoI6wjsCO0I7gjvCPAI8QjyCPMI9Aj1CPYI9wj4CPkI+gj7
+CPwI/Qj+CP8JAAkBCQIJAwkECQUJBgkHCQgJCQkKCQsJDAkNCQ4JDwkQCREJEgkTCRQJFQkWCRcJGAkZ
+CRoJGwkcCR0JHgkfCSAJIQkiCSMJJAklCSgJKwm8Ck0KTgpPClAKUQpSClMKVApVClYKVwpYClkKWgpb
+ClwKXQpeCl8KYAphCmIKYwpkCmUKZgpnCmgKaQpqCmsKbAptCm4KbwpwCnEKcgpzCnQKdQp2CncKeAp5
+CnoKewp8Cn0Kfgp/CoAKgQqCCoMKhAqFCoYKhwqICokKigqLCowKjQqOCo8KkAqRAQQKkgqTCpQKlQqW
+CpcKmAqZCpoKmwqcCp0KngqfCqAKoQqiCqMKpAqlCqYKpwqoCqkKqgqrCqwKrQquCq8KsAqxCrIKswq0
+CrUKtgq3CrgKuQq6AP8Kuwq8Cr0Kvgq/CsAKwQrCCsMKxArFCsYKxwrICskKygrLCswKzQrOCs8K0ArR
+CtIK0wrUCtUK1grXCtgK2QrcCt8K4lUkbnVsbN8QEgANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoA
+GwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMFZOU1Jvb3RWJGNsYXNzXU5T
+T2JqZWN0c0tleXNfEA9OU0NsYXNzZXNWYWx1ZXNfEBlOU0FjY2Vzc2liaWxpdHlPaWRzVmFsdWVzXU5T
+Q29ubmVjdGlvbnNbTlNOYW1lc0tleXNbTlNGcmFtZXdvcmtdTlNDbGFzc2VzS2V5c1pOU09pZHNLZXlz
+XU5TTmFtZXNWYWx1ZXNfEBlOU0FjY2Vzc2liaWxpdHlDb25uZWN0b3JzXU5TRm9udE1hbmFnZXJfEBBO
+U1Zpc2libGVXaW5kb3dzXxAPTlNPYmplY3RzVmFsdWVzXxAXTlNBY2Nlc3NpYmlsaXR5T2lkc0tleXNZ
+TlNOZXh0T2lkXE5TT2lkc1ZhbHVlc4ACgQLDgQF4gQIvgQLCgJeBAcyABYECLoECMIEBzYECwIAAgAaB
+AcuBAsERAYuBAjHSAA4AMgAzADRbTlNDbGFzc05hbWWABIADXU5TQXBwbGljYXRpb27SADcAOAA5ADpY
+JGNsYXNzZXNaJGNsYXNzbmFtZaIAOgA7Xk5TQ3VzdG9tT2JqZWN0WE5TT2JqZWN0XxAQSUJDb2NvYUZy
+YW1ld29ya9IADgA+AD8AQFpOUy5vYmplY3RzgJahAEGAB90AQwAOAEQARQBGAEcASABJAEoASwBMAE0A
+TgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgArXE5TV2luZG93Vmlld1xOU1NjcmVlblJlY3RfEBNOU0Zy
+YW1lQXV0b3NhdmVOYW1lXU5TV2luZG93VGl0bGVZTlNXVEZsYWdzXU5TV2luZG93Q2xhc3NfEBZOU1dp
+bmRvd0NvbnRlbnRNaW5TaXplXE5TV2luZG93UmVjdF8QD05TV2luZG93QmFja2luZ18QEU5TV2luZG93
+U3R5bGVNYXNrWU5TTWluU2l6ZVtOU1ZpZXdDbGFzc4AMgJWAkoCUgAkSdAAAAIAKgAuACBACEA+Ak4AA
+XxAYe3szMzUsIDM5Mn0sIHszNjQsIDM1OH19VldpbmRvd1hOU1dpbmRvd1p7MjUwLCAxNTB91wBhAA4A
+YgBjAGQAXgBlAGYAZwBoAGkAagBmAGxfEA9OU05leHRSZXNwb25kZXJaTlNTdWJ2aWV3c1hOU3ZGbGFn
+c1tOU0ZyYW1lU2l6ZVtOU1N1cGVydmlld4ANgJGADhEBAICPgA2AkNIADgA+AG4Ab4BrpABwAHEAcgBz
+gA+AIYAqgDDYAGEADgB1AHYAYwB3AF4AZQBPAHkAegB7AHwAfQBmAE9XTlNGcmFtZVZOU0NlbGxZTlNF
+bmFibGVkgAyAIIAQgBERAQwJgA2ADF8QFXt7NjAsIDMxNn0sIHs2MywgMjJ9fdoAggAOAIMAhACFAIYA
+hwCIAIkAigCLAIwAjQCOAI8AcACRAH0AkwCUW05TQ2VsbEZsYWdzXxARTlNCYWNrZ3JvdW5kQ29sb3Ja
+TlNDb250ZW50c1lOU1N1cHBvcnRdTlNDb250cm9sVmlld1xOU0NlbGxGbGFnczJfEBFOU0RyYXdzQmFj
+a2dyb3VuZF8QE05TUGxhY2Vob2xkZXJTdHJpbmdbTlNUZXh0Q29sb3IT/////5Rx/kGAH4AXgBKAE4AP
+EhBABAAJgBaAHFDUAA4AlwCYAJkAmgCbAJwAnVZOU1NpemVWTlNOYW1lWE5TZkZsYWdzgBUjQCoAAAAA
+AACAFBEEFFxMdWNpZGFHcmFuZGXSADcAOACgAKGiAKEAO1ZOU0ZvbnRTQW551QAOAKQApQCmAKcAqACp
+AKoAqwCsV05TQ29sb3JcTlNDb2xvclNwYWNlW05TQ29sb3JOYW1lXU5TQ2F0YWxvZ05hbWWAG4AaEAaA
+GYAYVlN5c3RlbV8QE3RleHRCYWNrZ3JvdW5kQ29sb3LTAA4ApQCwAKgAsgCzV05TV2hpdGWAGxADQjEA
+0gA3ADgAtQCkogCkADvVAA4ApAClAKYApwCoALgAqgC5AKyAG4AegB2AGFl0ZXh0Q29sb3LTAA4ApQCw
+AKgAsgC+gBtCMADSADcAOADAAMGkAMEAwgB2ADtfEA9OU1RleHRGaWVsZENlbGxcTlNBY3Rpb25DZWxs
+0gA3ADgAxADFpQDFAMYAxwDIADtbTlNUZXh0RmllbGRZTlNDb250cm9sVk5TVmlld1tOU1Jlc3BvbmRl
+ctgAYQAOAHUAdgBjAHcAXgBlAE8AeQDMAM0AfAB9AGYAT4AMgCCAIoAjCYANgAxfEBV7ezE3LCAzMTh9
+LCB7MzgsIDE3fX3YAIIADgCDAIQAhQCGAIcAigDTAIwA1QDWAI8AcQCRANkSBAH+QIAfgCWAJIATgCGA
+KFVQb3J0OtUADgCkAKUApgCnAKgA3QCqAN4ArIAbgCeAJoAYXGNvbnRyb2xDb2xvctMADgClALAAqACy
+AOOAG0swLjY2NjY2NjY5ANUADgCkAKUApgCnAKgAuACqAOcArIAbgB6AKYAYXxAQY29udHJvbFRleHRD
+b2xvctgAYQAOAHUAdgBjAHcAXgBlAE8A7ADtAO4A7wB9AGYAT4AMgC+AK4AsEQEJCYANgAxfEBZ7ezI1
+NCwgMzEwfSwgezk2LCAzMn193ACCAA4A9QD2APcA+ACEAIUAhgD5AIcA+gD7APwAjgD+AP8AjgEBAI8A
+cgEEAQUBBl8QE05TQWx0ZXJuYXRlQ29udGVudHNfEBJOU1BlcmlvZGljSW50ZXJ2YWxeTlNCdXR0b25G
+bGFnczJfEA9OU0tleUVxdWl2YWxlbnRfEA9OU1BlcmlvZGljRGVsYXldTlNCdXR0b25GbGFncxIEAf4A
+gC6AEhAZEIGAEoAtgBOAKhDIEggAAAAT/////4aCQP9VU3RhcnTSADcAOAEJAQqkAQoAwgB2ADtcTlNC
+dXR0b25DZWxs0gA3ADgBDAENpQENAMYAxwDIADtYTlNCdXR0b27cAGEBDwAOAHUBEABiAGMAXgBlAREB
+EgETAE8BFQEWARcBGAEZARoAZgBPAR0BHgEeW05TSFNjcm9sbGVyWE5Tc0ZsYWdzW05TVlNjcm9sbGVy
+XU5TTmV4dEtleVZpZXddTlNDb250ZW50Vmlld4AMgIuAjoCNEQISgDERARKADYAMgIeAMoAy0gAOAD4A
+bgEigGujAR4BHQEVgDKAh4CL3ABhAA4AdQEnASgAYgBjASkAXgEqAGUBEgBzASwBLQEuAS8BMAExATIA
+ZgCpAHMBMlhOU0N1cnNvcllOU2N2RmxhZ3NZTlNEb2NWaWV3WU5TQkdDb2xvcoAwgIaAgoCDEASAMxEJ
+AIA0gA2AGoAwgDTSAA4APgBuATmAa6EBMoA03ABhAA4BPAE9AT4AYwBkAT8AXgFAAGUBQQEeAUMBRACq
+AUUBRgFHAUgAZgFKAR4AK18QD05TVGV4dENvbnRhaW5lcllOU1RWRmxhZ3NcTlNTaGFyZWREYXRhWU5T
+TWF4U2l6ZVhOU01pbml6ZVpOU0RlbGVnYXRlgDKAgYA2gHIRKRKANYB/gA2AgIAygABaezMyMiwgMTIw
+fdUBTwAOAVABUQFSAVMBVAEyAVYBV1lOU1RDRmxhZ3NaTlNUZXh0Vmlld1dOU1dpZHRoXxAPTlNMYXlv
+dXRNYW5hZ2VyEAGAcYA0I0B0IAAAAAAAgDfVAA4BWQFaAVsBQQFcAV0BXgCqACtfEBBOU1RleHRDb250
+YWluZXJzXU5TVGV4dFN0b3JhZ2VZTlNMTUZsYWdzgHCAb4A4gADVAA4BYQFiAWMBQQFkAWUBZgFnACtc
+TlNBdHRyaWJ1dGVzWE5TU3RyaW5nXxAPTlNBdHRyaWJ1dGVJbmZvgG6AO4A5gGyAANIADgFqAWsBbFlO
+Uy5zdHJpbmeAOl8RAl5Mb3JlbSBpcHN1bSBkb2xvciBzaXQgZXIgZWxpdCBsYW1ldCwgY29uc2VjdGV0
+YXVyIGNpbGxpdW0gYWRpcGlzaWNpbmcgcGVjdSwgc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1
+bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVuaWFt
+LCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0aW9uIHVsbGFtY28gbGFib3JpcyBuaXNpIHV0IGFsaXF1aXAg
+ZXggZWEgY29tbW9kbyBjb25zZXF1YXQuIER1aXMgYXV0ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRl
+cml0IGluIHZvbHVwdGF0ZSB2ZWxpdCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBh
+cmlhdHVyLiBFeGNlcHRldXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBzdW50
+IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVtIEV0
+IGhhcnVtZCB1bmQgbG9va3VtIGxpa2UgR3JlZWsgdG8gbWUsIGRlcmV1ZCBmYWNpbGlzIGVzdCBlciBl
+eHBlZGl0IGRpc3RpbmN0LiBOYW0gbGliZXIgdGUgY29uc2NpZW50IHRvIGZhY3RvciB0dW0gcG9lbiBs
+ZWd1bSBvZGlvcXVlIGNpdml1ZGHSADcAOAFuAW+jAW8BYgA7XxAPTlNNdXRhYmxlU3RyaW5n0gAOAD4A
+bgFygGuiAXMBdIA8gGbTAA4BdgA+AXcBeAF7V05TLmtleXOAZaIBeQF6gD2APqIBfAF9gD+AZF8QEE5T
+UGFyYWdyYXBoU3R5bGXTAYAADgGBAYIBgwCyWk5TVGFiU3RvcHNbTlNBbGlnbm1lbnSAQIBj0gAOAD4B
+hQGGgGKvECABhwGIAYkBigGLAYwBjQGOAY8BkAGRAZIBkwGUAZUBlgGXAZgBmQGaAZsBnAGdAZ4BnwGg
+AaEBogGjAaQBpQGmgEGAQ4BEgEWARoBHgEiASYBKgEuATIBNgE6AT4BQgFGAUoBTgFSAVYBWgFeAWIBZ
+gFqAW4BcgF2AXoBfgGCAYdIADgGoAakBqlpOU0xvY2F0aW9ugEIjAAAAAAAAAADSADcAOAGsAa2iAa0A
+O1lOU1RleHRUYWLSAA4BqAGpAbCAQiNATAAAAAAAANIADgGoAakBs4BCI0BcAAAAAAAA0gAOAagBqQG2
+gEIjQGUAAAAAAADSAA4BqAGpAbmAQiNAbAAAAAAAANIADgGoAakBvIBCI0BxgAAAAAAA0gAOAagBqQG/
+gEIjQHUAAAAAAADSAA4BqAGpAcKAQiNAeIAAAAAAANIADgGoAakBxYBCI0B8AAAAAAAA0gAOAagBqQHI
+gEIjQH+AAAAAAADSAA4BqAGpAcuAQiNAgYAAAAAAANIADgGoAakBzoBCI0CDQAAAAAAA0gAOAagBqQHR
+gEIjQIUAAAAAAADSAA4BqAGpAdSAQiNAhsAAAAAAANIADgGoAakB14BCI0CIgAAAAAAA0gAOAagBqQHa
+gEIjQIpAAAAAAADSAA4BqAGpAd2AQiNAjAAAAAAAANIADgGoAakB4IBCI0CNwAAAAAAA0gAOAagBqQHj
+gEIjQI+AAAAAAADSAA4BqAGpAeaAQiNAkKAAAAAAANIADgGoAakB6YBCI0CRgAAAAAAA0gAOAagBqQHs
+gEIjQJJgAAAAAADSAA4BqAGpAe+AQiNAk0AAAAAAANIADgGoAakB8oBCI0CUIAAAAAAA0gAOAagBqQH1
+gEIjQJUAAAAAAADSAA4BqAGpAfiAQiNAleAAAAAAANIADgGoAakB+4BCI0CWwAAAAAAA0gAOAagBqQH+
+gEIjQJegAAAAAADSAA4BqAGpAgGAQiNAmIAAAAAAANIADgGoAakCBIBCI0CZYAAAAAAA0gAOAagBqQIH
+gEIjQJpAAAAAAADSAA4BqAGpAgqAQiNAmyAAAAAAANIANwA4AgwCDaICDQA7V05TQXJyYXnSADcAOAIP
+AX6iAX4AO9QADgCXAJgAmQCaAhIAnAIUgBUjQCQAAAAAAACAFBELG9IANwA4AhYCF6ICFwA7XE5TRGlj
+dGlvbmFyedMADgF2AD4BdwIaAh2AZaIBeQF6gD2APqICHgIfgGeAadMBgAAOAYECIQGDALKAaIBj0gAO
+AD4BhQIlgGKvECABhwGIAYkBigGLAYwBjQGOAY8BkAGRAZIBkwGUAZUBlgGXAZgBmQGaAZsBnAGdAZ4B
+nwGgAaEBogGjAaQBpQGmgEGAQ4BEgEWARoBHgEiASYBKgEuATIBNgE6AT4BQgFGAUoBTgFSAVYBWgFeA
+WIBZgFqAW4BcgF2AXoBfgGCAYdQADgCXAJgAmQCaAhICSAJJgBWAahAQXxARTHVjaWRhR3JhbmRlLUJv
+bGTSADcAOAJMAk2jAk0CDQA7Xk5TTXV0YWJsZUFycmF50gAOAk8CUAJRV05TLmRhdGGAbU8QDxkABAEI
+AAwBIgAHAYQEANIANwA4AlMCVKMCVAJVADtdTlNNdXRhYmxlRGF0YVZOU0RhdGHSADcAOAJXAVqkAVoC
+WAJZADtfEBlOU011dGFibGVBdHRyaWJ1dGVkU3RyaW5nXxASTlNBdHRyaWJ1dGVkU3RyaW5n0gAOAD4A
+bgJcgGuhAUSANtIANwA4Al8BUqIBUgA70gA3ADgCYQE8ogE8ADvYAA4CYwJkAmUAgwJmAmcCaAJpAmoA
+KwC4AKkCbgArAnBXTlNGbGFnc18QF05TRGVmYXVsdFBhcmFncmFwaFN0eWxlXxAQTlNJbnNlcnRpb25D
+b2xvcl8QFE5TU2VsZWN0ZWRBdHRyaWJ1dGVzXxASTlNNYXJrZWRBdHRyaWJ1dGVzXxAQTlNMaW5rQXR0
+cmlidXRlc4B+EStlgACAHoAagHOAAIB60wAOAXYAPgF3AnMCdoBlogJ0AnWAdIB1ogJ3AniAdoB41QAO
+AKQApQCmAKcAqADdAKoCfACsgBuAJ4B3gBhfEBtzZWxlY3RlZFRleHRCYWNrZ3JvdW5kQ29sb3LVAA4A
+pAClAKYApwCoALgAqgKCAKyAG4AegHmAGF8QEXNlbGVjdGVkVGV4dENvbG9y0wAOAXYAPgF3AocCioBl
+ogKIAnWAe4B1ogKLAoyAfIB9W05TVW5kZXJsaW5l0wAOAKUCjwCoAVMCkVVOU1JHQoAbRjAgMCAxANIA
+NwA4ApMClKIClAA7XxAUTlNUZXh0Vmlld1NoYXJlZERhdGFcezQ1NCwgMWUrMDd9V3s4NCwgMH3SADcA
+OAKYAVClAVACmQDHAMgAO1ZOU1RleHRfEBR7ezEsIDF9LCB7MzIyLCAyODZ9fdMADgKcAp0CngKfAVNZ
+TlNIb3RTcG90XE5TQ3Vyc29yVHlwZYCFgIRXezQsIC01fdIANwA4AqIBJ6IBJwA70gA3ADgCpAKlpAKl
+AMcAyAA7Wk5TQ2xpcFZpZXfZAGECpwAOAHUAYwBeAqgAZQKpAHMAcwKsAq0CrgBmArAAcwKyWE5TVGFy
+Z2V0WE5TQWN0aW9uWU5TUGVyY2VudIAwgDCAioCIE/////+AAAEAgA2AiYAwIz/W6JxgAAAAXxAVe3sz
+MDgsIDF9LCB7MTUsIDI4Nn19XF9kb1Njcm9sbGVyOtIANwA4ArYCt6UCtwDGAMcAyAA7Wk5TU2Nyb2xs
+ZXLbAGECpwAOAHUBEABjAF4CqABlArkCqQBzAHMCrAK9AVMAaQBmArAAcwLBAsJaTlNDdXJWYWx1ZYAw
+gDCAioCMgA2AiYAwIz/wAAAAAAAAIz/uQshgAAAAXxAYe3stMTAwLCAtMTAwfSwgezg3LCAxOH19XxAW
+e3syMCwgMjB9LCB7MzI0LCAyODh9fdIANwA4AsYCx6QCxwDHAMgAO1xOU1Njcm9sbFZpZXdaezM2NCwg
+MzU4fdIANwA4AsoAx6MAxwDIADtfEBZ7ezAsIDB9LCB7MTY4MCwgMTAyOH19WnsyNTAsIDE3Mn1fEBBF
+Y2hvU2VydmVyV2luZG930gA3ADgCzwLQogLQADtfEBBOU1dpbmRvd1RlbXBsYXRl0gA3ADgC0gLTowLT
+AtQAO1xOU011dGFibGVTZXRVTlNTZXTSAA4APgBuAteAa68QLgLYAtkC2gLbAtwC3QLeAt8C4ALhAuIC
+4wLkAuUC5gLnAugC6QLqAusC7ALtAu4C7wLwAvEC8gLzAvQC9QL2AvcC+AL5AvoC+wL8Av0C/gL/AwAD
+AQMCAwMDBAMFgJiApoCsgLGAtoC8gMGAxoDMgNGA1oDagN6A4oDngOyA8YD2gPqBAQCBAQaBAQuBARCB
+ARWBARqBAR+BASOBASiBAS2BATGBATSBATqBAT6BAUKBAUeBAUmBAU6BAVOBAVWBAVmBAVyBAWCBAWWB
+AWeBAWyBAXHTAA4DBwMIAwkDCgMLWE5TU291cmNlV05TTGFiZWyApYCZgKTZAA4DDQMOAw8DEAMRAxID
+EwMUAxUDFgMXAxgDGQMaAxsDHAFTV05TVGl0bGVfEBFOU0tleUVxdWl2TW9kTWFza1pOU0tleUVxdWl2
+XU5TTW5lbW9uaWNMb2NZTlNPbkltYWdlXE5TTWl4ZWRJbWFnZVZOU01lbnVVTlNUYWeAo4CbEgAQAACA
+nBJ/////gJ2AoYCa0wAOAw0DHgMfAyADIVtOU01lbnVJdGVtc4EBiYEBv4EBwV8QEFNtYXJ0IENvcHkv
+UGFzdGVRZtMADgAyAyUDJgMnAyheTlNSZXNvdXJjZU5hbWWAoICegJ9XTlNJbWFnZV8QD05TTWVudUNo
+ZWNrbWFya9IANwA4AywDLaIDLQA7XxAQTlNDdXN0b21SZXNvdXJjZdMADgAyAyUDJgMnAzGAoICegKJf
+EBBOU01lbnVNaXhlZFN0YXRl0gA3ADgDNAM1ogM1ADtaTlNNZW51SXRlbV8QGHRvZ2dsZVNtYXJ0SW5z
+ZXJ0RGVsZXRlOtIANwA4AzgDOaMDOQM6ADtfEBVOU05pYkNvbnRyb2xDb25uZWN0b3JeTlNOaWJDb25u
+ZWN0b3LTAA4DBwMIAwkDPQM+gKWAp4Cr2AAOAw0DDgMPAxADEQMSAxMDFQNBAxcDQgMZAxoDGwNFgKOA
+qYCqgJ2AoYCo0wAOAw0DHgMfA0gDSYEBiYEBr4EBsWUATwBwAGUAbiAmUW9db3BlbkRvY3VtZW50OtMA
+DgMHAwgDCQNPA1CApYCtgLDYAA4DDQMOAw8DEAMRAxIDEwMVA1MDFwCOAxkDGgMbA1eAo4CvgBKAnYCh
+gK7UAA4DDQCYAx4DHwNaA1sDXIEBiYEBe4EBjIEBfVhTaG93IEFsbF8QFnVuaGlkZUFsbEFwcGxpY2F0
+aW9uczrTAA4DBwMIAwkDYQNigKWAsoC12AAOAw0DDgMPAxADEQMSAxMDFQNlA2YDZwMZAxoDGwNFgKOA
+sxIAEgAAgLSAnYChgKhoAFMAYQB2AGUAIABBAHMgJlFTXxAPc2F2ZURvY3VtZW50QXM60wAOAwcDCAMJ
+A3ADcYClgLeAu9gADgMNAw4DDwMQAxEDEgMTAxUDdAMXA3UDGQMaAxsDeICjgLmAuoCdgKGAuNMADgMN
+Ax4DHwN7A3yBAYmBAaiBAalfEBNOZXdBcHBsaWNhdGlvbiBIZWxwUT9Zc2hvd0hlbHA60wAOAwcDCAMJ
+A4IDg4ClgL2AwNgADgMNAw4DDwMQAxEDEgMTAxUDhgMXAI4DGQMaAxsDioCjgL+AEoCdgKGAvtQADgMN
+AJgDHgMfA40DjgOPgQGJgQGqgQGsgQGrWkNsZWFyIE1lbnVfEBVjbGVhclJlY2VudERvY3VtZW50czrT
+AA4DBwMIAwkDlAOVgKWAwoDF2QAOA5cDDQMOAw8DEAMRAxIDEwMVAI4DmgNmA5sDGQMaAxsDRVlOU1Rv
+b2xUaXCAo4ASgMOAxICdgKGAqF1QYWdlIFNldHVwLi4uUVBecnVuUGFnZUxheW91dDrTAA4DBwMIAwkD
+pAOlgKWAx4DL2AAOAw0DDgMPAxADEQMSAxMDFQOoAxcDqQMZAxoDGwOsgKOAyYDKgJ2AoYDI0wAOAw0D
+HgMfA68DsIEBiYEBkoEBk18QEUp1bXAgdG8gU2VsZWN0aW9uUWpfEB1jZW50ZXJTZWxlY3Rpb25JblZp
+c2libGVBcmVhOtMADgMHAwgDCQO2A7eApYDNgNDYAA4DDQMOAw8DEAMRAxIDEwMVA7oDFwCOAxkDGgMb
+A76Ao4DPgBKAnYChgM7TAA4DDQMeAx8DwQPCgQGJgQGlgQGnXVN0b3AgU3BlYWtpbmddc3RvcFNwZWFr
+aW5nOtMADgMHAwgDCQPHA8iApYDSgNXYAA4DDQMOAw8DEAMRAxIDEwMVA8sDFwPMAxkDGgMbA1eAo4DT
+gNSAnYChgK5fEBNRdWl0IE5ld0FwcGxpY2F0aW9uUXFadGVybWluYXRlOtMADgMHAwgDCQPVA9aApYDX
+gNnYAA4DDQMOAw8DEAMRAxIDEwMVA9kDFwCOAxkDGgMbA76Ao4DYgBKAnYChgM5eU3RhcnQgU3BlYWtp
+bmdec3RhcnRTcGVha2luZzrTAA4DBwMIAwkD4gPjgKWA24Dd1wAOAw0DDwMQAxEDEgMTAxUD5gCOAxkD
+GgMbA0WAo4DcgBKAnYChgKhfEA9SZXZlcnQgdG8gU2F2ZWRfEBZyZXZlcnREb2N1bWVudFRvU2F2ZWQ6
+1AAOA+4DBwMIAwkAHwPxA/JdTlNEZXN0aW5hdGlvboClgAKA34Dh1wAOAw0DDwMQAxEDEgMTAxUD9QCO
+AxkDGgMbA1eAo4DggBKAnYChgK5fEBRBYm91dCBOZXdBcHBsaWNhdGlvbl8QHW9yZGVyRnJvbnRTdGFu
+ZGFyZEFib3V0UGFuZWw60wAOAwcDCAMJA/4D/4ClgOOA5tgADgMNAw4DDwMQAxEDEgMTAxUEAgMXAI4D
+GQMaAxsEBoCjgOWAEoCdgKGA5NMADgMNAx4DHwQJBAqBAYmBAZuBAZxvEBIAQwB1AHMAdABvAG0AaQB6
+AGUAIABUAG8AbwBsAGIAYQByICZfEB9ydW5Ub29sYmFyQ3VzdG9taXphdGlvblBhbGV0dGU60wAOAwcD
+CAMJBA8EEIClgOiA69gADgMNAw4DDwMQAxEDEgMTAxUEEwQUBBUDGQMaAxsEBoCjgOkSABgAAIDqgJ2A
+oYDkXFNob3cgVG9vbGJhclF0XxATdG9nZ2xlVG9vbGJhclNob3duOtMADgMHAwgDCQQeBB+ApYDtgPDY
+AA4DDQMOAw8DEAMRAxIDEwMVBCIDFwCOAxkDGgMbBCaAo4DvgBKAnYChgO7UAA4DDQCYAx4DHwBTBCoE
+K4EBiYAJgQGRgQGPVFpvb21ccGVyZm9ybVpvb2060wAOAwcDCAMJBDAEMYClgPKA9dgADgMNAw4DDwMQ
+AxEDEgMTAxUENAMXBDUDGQMaAxsDV4CjgPOA9ICdgKGArl8QE0hpZGUgTmV3QXBwbGljYXRpb25RaFVo
+aWRlOtMADgMHAwgDCQQ+BD+ApYD3gPnYAA4DDQMOAw8DEAMRAxIDEwMVBEIDFwCOAxkDGgMbBCaAo4D4
+gBKAnYChgO5fEBJCcmluZyBBbGwgdG8gRnJvbnRfEA9hcnJhbmdlSW5Gcm9udDrTAA4DBwMIAwkESwRM
+gKWA+4D/2AAOAw0DDgMPAxADEQMSAxMDFQRPAxcEUAMZAxoDGwRTgKOA/YD+gJ2AoYD80wAOAw0DHgMf
+BFYEV4EBiYEBu4EBvW4AUwBoAG8AdwAgAFMAcABlAGwAbABpAG4AZyAmUTpfEA9zaG93R3Vlc3NQYW5l
+bDrTAA4DBwMIAwkEXQRegKWBAQGBAQXYAA4DDQMOAw8DEAMRAxIDEwMVBGEDFwRiAxkDGgMbBGWAo4EB
+A4EBBICdgKGBAQLTAA4DDQMeAx8EaARpgQGJgQG1gQG2VVBhc3RlUXZWcGFzdGU60wAOAwcDCAMJBG8E
+cIClgQEHgQEK2AAOAw0DDgMPAxADEQMSAxMDFQRzAxcEdAMZAxoDGwRlgKOBAQiBAQmAnYChgQECWlNl
+bGVjdCBBbGxRYVpzZWxlY3RBbGw60wAOAwcDCAMJBH0EfoClgQEMgQEP2AAOAw0DDgMPAxADEQMSAxMD
+FQSBAxcEggMZAxoDGwRTgKOBAQ2BAQ6AnYChgPxeQ2hlY2sgU3BlbGxpbmdRO15jaGVja1NwZWxsaW5n
+OtMADgMHAwgDCQSLBIyApYEBEYEBFNgADgMNAw4DDwMQAxEDEgMTAxUEjwNmBJADGQMaAxsEZYCjgQES
+gQETgJ2AoYEBAlRSZWRvUVpVcmVkbzrTAA4DBwMIAwkEmQSagKWBARaBARnZAA4DDQMOAw8DEAMRAxID
+EwMUAxUEnQNmBJ4DGQMaAxsDHACygKOBAReBARiAnYChgJpbU21hcnQgTGlua3NRR18QHXRvZ2dsZUF1
+dG9tYXRpY0xpbmtEZXRlY3Rpb2460wAOAwcDCAMJBKcEqIClgQEbgQEe2AAOAw0DDgMPAxADEQMSAxMD
+FQSrAxcErAMZAxoDGwRlgKOBARyBAR2AnYChgQECU0N1dFF4VGN1dDrTAA4DBwMIAwkEtQS2gKWBASCB
+ASLYAA4DDQMOAw8DEAMRAxIDEwMVBLkDFwCOAxkDGgMbBGWAo4EBIYASgJ2AoYEBAlZEZWxldGVXZGVs
+ZXRlOtMADgMHAwgDCQTCBMOApYEBJIEBJ9kADgMNAw4DDwMQAxEDEgMTAxQDFQTGAxcExwMZAxoDGwMc
+AFiAo4EBJYEBJoCdgKGAmlxTbWFydCBRdW90ZXNRZ18QIXRvZ2dsZUF1dG9tYXRpY1F1b3RlU3Vic3Rp
+dHV0aW9uOtMADgMHAwgDCQTQBNGApYEBKYEBLNgADgMNAw4DDwMQAxEDEgMTAxUE1AMXBNUDGQMaAxsD
+RYCjgQEqgQErgJ2AoYCoZgBQAHIAaQBuAHQgJlFwVnByaW50OtMADgMHAwgDCQTeBN+ApYEBLoEBMNkA
+DgMNAw4DDwMQAxEDEgMTAxQDFQTiAxcDGAMZAxoDGwOsAVOAo4EBL4CcgJ2AoYDIZQBGAGkAbgBkICZf
+EBdwZXJmb3JtRmluZFBhbmVsQWN0aW9uOtQADgPuAwcDCATqAHIAQQTtgQEzgCqAB4EBMl8QFWluaXRp
+YWxGaXJzdFJlc3BvbmRlctIANwA4BPAE8aME8QM6ADtfEBROU05pYk91dGxldENvbm5lY3RvctMADgMH
+AwgDCQT0BPWApYEBNYEBOdgADgMNAw4DDwMQAxEDEgMTAxUE+ANmBPkDGQMaAxsE/ICjgQE3gQE4gJ2A
+oYEBNtMADgMNAx4DHwT/BQCBAYmBAZ6BAaBbU2hvdyBDb2xvcnNRQ18QFW9yZGVyRnJvbnRDb2xvclBh
+bmVsOtQADgPuAwcDCATqAHAFBwUIgQEzgA+BATuBAT3SAA4AMgAzBQuABIEBPF1BcHBDb250cm9sbGVy
+WXBvcnRGaWVsZNMADgMHAwgDCQUQBRGApYEBP4EBQdgADgMNAw4DDwMQAxEDEgMTAxUFFAMXAI4DGQMa
+AxsEU4CjgQFAgBKAnYChgPxfEBtDaGVjayBHcmFtbWFyIFdpdGggU3BlbGxpbmdfEBZ0b2dnbGVHcmFt
+bWFyQ2hlY2tpbmc60wAOAwcDCAMJBR0FHoClgQFDgQFG2AAOAw0DDgMPAxADEQMSAxMDFQUhAxcFIgMZ
+AxoDGwNFgKOBAUSBAUWAnYChgKhTTmV3UW5cbmV3RG9jdW1lbnQ61AAOA+4DBwMIBOoAcgUHBS2BATOA
+KoEBO4EBSF8QD3N0YXJ0U3RvcEJ1dHRvbtMADgMHAwgDCQUxBTKApYEBSoEBTdgADgMNAw4DDwMQAxED
+EgMTAxUFNQMXBTYDGQMaAxsEJoCjgQFLgQFMgJ2AoYDuWE1pbmltaXplUW1fEBNwZXJmb3JtTWluaWF0
+dXJpemU60wAOAwcDCAMJBT8FQIClgQFPgQFS2AAOAw0DDgMPAxADEQMSAxMDFQVDAxcFRAMZAxoDGwRl
+gKOBAVCBAVGAnYChgQECVFVuZG9RelV1bmRvOtQADgPuAwcDCAMJBQcAcgVPgKWBATuAKoEBVFpzdGFy
+dFN0b3A60wAOAwcDCAMJBVMFVIClgQFWgQFY2AAOAw0DDgMPAxADEQMSAxMDFQVXBBQENQMZAxoDGwNX
+gKOBAVeA9ICdgKGArltIaWRlIE90aGVyc18QFmhpZGVPdGhlckFwcGxpY2F0aW9uczrUAA4D7gMHAwgE
+6gUHBWEFYoEBM4EBO4EBWoEBW9IADgAyADMANIAEgANYZGVsZWdhdGXTAA4DBwMIAwkFaQVqgKWBAV2B
+AV/YAA4DDQMOAw8DEAMRAxIDEwMVBW0DFwCOAxkDGgMbBFOAo4EBXoASgJ2AoYD8XxAbQ2hlY2sgU3Bl
+bGxpbmcgV2hpbGUgVHlwaW5nXxAedG9nZ2xlQ29udGludW91c1NwZWxsQ2hlY2tpbmc60wAOAwcDCAMJ
+BXYFd4ClgQFhgQFk2AAOAw0DDgMPAxADEQMSAxMDFQV6AxcFewMZAxoDGwNFgKOBAWKBAWOAnYChgKhV
+Q2xvc2VRd11wZXJmb3JtQ2xvc2U61AAOA+4DBwMIBOoBMgUHBYaBATOANIEBO4EBZldsb2dWaWV30wAO
+AwcDCAMJBYoFi4ClgQFogQFr2AAOAw0DDgMPAxADEQMSAxMDFQWOAxcFjwMZAxoDGwRlgKOBAWmBAWqA
+nYChgQECVENvcHlRY1Vjb3B5OtMADgMHAwgDCQWYBZmApYEBbYEBcNgADgMNAw4DDwMQAxEDEgMTAxUF
+nAMXBZ0DGQMaAxsDRYCjgQFugQFvgJ2AoYCoVFNhdmVRc11zYXZlRG9jdW1lbnQ61wAOA+4FpQWmAwcD
+CAWnBagFqQWqBasAcAWtAFhZTlNLZXlQYXRoWU5TQmluZGluZ18QHE5TTmliQmluZGluZ0Nvbm5lY3Rv
+clZlcnNpb26BAXeBAXKBAXaBAXWAD4EBdNIADgWvBbAAfV8QEE5TU2hhcmVkSW5zdGFuY2WBAXMJ0gA3
+ADgFswW0owW0BbUAO18QGE5TVXNlckRlZmF1bHRzQ29udHJvbGxlclxOU0NvbnRyb2xsZXJfEBh2YWx1
+ZTogdmFsdWVzLnNlcnZlclBvcnRVdmFsdWVfEBF2YWx1ZXMuc2VydmVyUG9ydNIANwA4BboFu6MFuwM6
+ADtfEBVOU05pYkJpbmRpbmdDb25uZWN0b3LSAA4APgGFBb6AYq8QXwT0A08ESwXCAz0FxAXFBMIFxwSn
+BckDrANhAHsFzQSLAwoFYQPiBAYF0wXUBdUF1gQwAHMF2QN4A4oE/AOkBd4D8QOUBF0F4gBPBGUDtgXm
+AM0EmQQ+BXYAcgEdA3AFigEVBVMF8QBBBakF9AX1AHABMgNXBfkF+gX7BRAFmAOCBG8D/gBxBgIA7gS1
+BB4DxwTQBggEJgYKA0UDHAYNBQcGDwUxBT8FHQQPBhQEUwYWBhcE3gVpBH0DvgPVBh2BATWArYD7gQF5
+gKeBAY2BAYOBASSBAZCBARuBAYqAyICygBGBAYaBARGAmYEBWoDbgOSBAZ2BAaOBAaSBAYuA8oAwgQGh
+gLiAvoEBNoDHgQGtgN+AwoEBAYEBroAMgQECgM2BAb6AI4EBFoD3gQFhgCqAh4C3gQFogIuBAVaBAbqA
+B4EBcoEBuIEBwoAPgDSAroEBtIEBf4EBlIEBP4EBbYC9gQEHgOOAIYEBloAsgQEggO2A0oEBKYEBt4Du
+gQHEgKiAmoEBxoEBO4EBmIEBSoEBT4EBQ4DogQF+gPyBAbKBAXqBAS6BAV2BAQyAzoDXgQGC2gAOBh8D
+DQMOAw8DEAMRAxIDEwKoAxUDVwNaAxcAjgMZAxoDGwYXBidZTlNTdWJtZW51gKOAroEBe4ASgJ2AoYEB
+eoEBfNQADgMNAJgDHgMfBioGKwYsgQGJgQHIgQHKgQHJXk5ld0FwcGxpY2F0aW9uXnN1Ym1lbnVBY3Rp
+b2460gAOAD4AbgYxgGurA/EGFAX6Bh0FxQXJBDAFUwNPBdYDx4DfgQF+gQF/gQGCgQGDgQGKgPKBAVaA
+rYEBi4DS2gAOAw0DDgY+Aw8GPwMQAxEDEgMTAxUAjgMXAH0AjgB9AxkDGgMbA1ddTlNJc1NlcGFyYXRv
+clxOU0lzRGlzYWJsZWSAo4ASCYASCYCdgKGArtgADgMNAw4DDwMQAxEDEgMTAxUGSgMXBksDGQMaAxsD
+V4CjgQGAgQGBgJ2AoYCubABQAHIAZQBmAGUAcgBlAG4AYwBlAHMgJlEs2gAOAw0DDgY+Aw8GPwMQAxED
+EgMTAxUAjgMXAH0AjgB9AxkDGgMbA1eAo4ASCYASCYCdgKGArtoADgYfAw0DDgMPAxADEQMSAxMCqAMV
+Bc0GXQMXAI4DGQMaAxsDVwZigKOBAYaBAYSAEoCdgKGAroEBhVhTZXJ2aWNlc9QADgMNAJgDHgMfBl0G
+ZwZogQGJgQGEgQGIgQGH0gAOAD4AbgZrgGugXxAPX05TU2VydmljZXNNZW510gA3ADgGbgMTogMTADva
+AA4DDQMOBj4DDwY/AxADEQMSAxMDFQCOAxcAfQCOAH0DGQMaAxsDV4CjgBIJgBIJgJ2AoYCu2gAOAw0D
+DgY+Aw8GPwMQAxEDEgMTAxUAjgMXAH0AjgB9AxkDGgMbA1eAo4ASCYASCYCdgKGArlxfTlNBcHBsZU1l
+bnXaAA4GHwMNAw4DDwMQAxEDEgMTAqgDFQQmAFMDFwCOAxkDGgMbBhcGioCjgO6ACYASgJ2AoYEBeoEB
+jtIADgA+AG4GjYBrpAUxBB4FxwQ+gQFKgO2BAZCA99oADgMNAw4GPgMPBj8DEAMRAxIDEwMVAI4DFwB9
+AI4AfQMZAxoDGwQmgKOAEgmAEgmAnYChgO5eX05TV2luZG93c01lbnVURmluZNIADgA+AG4Gn4BrpQTe
+BfsGAgYPA6SBAS6BAZSBAZaBAZiAx9kADgMNAw4DDwMQAxEDEgMTAxQDFQanAxcExwMZAxoDGwOsAFiA
+o4EBlYEBJoCdgKGAyFlGaW5kIE5leHTZAA4DDQMOAw8DEAMRAxIDEwMUAxUGrwNmBJ4DGQMaAxsDrACy
+gKOBAZeBARiAnYChgMhdRmluZCBQcmV2aW91c9kADgMNAw4DDwMQAxEDEgMTAxQDFQa3AxcGuAMZAxoD
+GwOsBryAo4EBmYEBmoCdgKGAyBAHXxAWVXNlIFNlbGVjdGlvbiBmb3IgRmluZFFlVFZpZXfSAA4APgBu
+BsKAa6IEDwP+gOiA49oADgYfAw0DDgMPAxADEQMSAxMCqAMVBPwE/wMXAI4DGQMaAxsGFwbNgKOBATaB
+AZ6AEoCdgKGBAXqBAZ9WRm9ybWF00gAOAD4AbgbRgGuiBdkE9IEBoYEBNdgADgMNAw4DDwMQAxEDEgMT
+AxUG1gMXBBUDGQMaAxsE/ICjgQGigOqAnYChgQE2WlNob3cgRm9udHPaAA4DDQMOBj4DDwY/AxADEQMS
+AxMDFQCOAxcAfQCOAH0DGQMaAxsDRYCjgBIJgBIJgJ2AoYCo2gAOBh8DDQMOAw8DEAMRAxIDEwKoAxUD
+vgPBAxcAjgMZAxoDGwRlBu2Ao4DOgQGlgBKAnYChgQECgQGmVlNwZWVjaNIADgA+AG4G8YBrogPVA7aA
+14DNVEhlbHDSAA4APgBuBveAa6EDcIC3W09wZW4gUmVjZW500gAOAD4Abgb8gGuhA4KAvV8QFl9OU1Jl
+Y2VudERvY3VtZW50c01lbnXaAA4DDQMOBj4DDwY/AxADEQMSAxMDFQCOAxcAfQCOAH0DGQMaAxsEZYCj
+gBIJgBIJgJ2AoYEBAtoADgYfAw0DDgMPAxADEQMSAxMCqAMVA0UDSAMXAI4DGQMaAxsGFwcQgKOAqIEB
+r4ASgJ2AoYEBeoEBsFRGaWxl0gAOAD4AbgcUgGurBR0DPQYWBfkFdgWYA2ED4gXUA5QE0IEBQ4CngQGy
+gQG0gQFhgQFtgLKA24EBo4DCgQEp2gAOBh8DDQMOAw8DEAMRAxIDEwKoAxUDigONAxcAjgMZAxoDGwNF
+ByiAo4C+gQGqgBKAnYChgKiBAbPaAA4DDQMOBj4DDwY/AxADEQMSAxMDFQCOAxcAfQCOAH0DGQMaAxsD
+RYCjgBIJgBIJgJ2AoYCoVEVkaXTSAA4APgBuBzWAa60FPwSLBd4EpwWKBF0EtQRvBggF9AXxBeYF1YEB
+T4EBEYEBrYEBG4EBaIEBAYEBIIEBB4EBt4EBuIEBuoEBvoEBpNoADgMNAw4GPgMPBj8DEAMRAxIDEwMV
+AI4DFwB9AI4AfQMZAxoDGwRlgKOAEgmAEgmAnYChgQEC2gAOBh8DDQMOAw8DEAMRAxIDEwKoAxUDrAOv
+AxcAjgMZAxoDGwRlB1SAo4DIgQGSgBKAnYChgQECgQG52gAOBh8DDQMOAw8DEAMRAxIDEwKoAxUEUwRW
+AxcAjgMZAxoDGwRlB12Ao4D8gQG7gBKAnYChgQECgQG8XxAUU3BlbGxpbmcgYW5kIEdyYW1tYXLSAA4A
+PgBuB2GAa6QESwR9BWkFEID7gQEMgQFdgQE/2gAOBh8DDQMOAw8DEAMRAxIDEwKoAxUDHAMgAxcAjgMZ
+AxoDGwRlB26Ao4CagQG/gBKAnYChgQECgQHAXVN1YnN0aXR1dGlvbnPSAA4APgBuB3KAa6MDCgTCBJmA
+mYEBJIEBFtoADgYfAw0DDgMPAxADEQMSAxMCqAMVBGUEaAMXAI4DGQMaAxsGFwd+gKOBAQKBAbWAEoCd
+gKGBAXqBAcPaAA4GHwMNAw4DDwMQAxEDEgMTAqgDFQQGBAkDFwCOAxkDGgMbBhcHh4CjgOSBAZuAEoCd
+gKGBAXqBAcXaAA4GHwMNAw4DDwMQAxEDEgMTAqgDFQN4A3sDFwCOAxkDGgMbBhcHkICjgLiBAaiAEoCd
+gKGBAXqBAcdZQU1haW5NZW510gAOAD4AbgeUgGunBcIF4gX1BdMGCgXEBg2BAXmBAa6BAcKBAZ2BAcSB
+AY2BAcZbX05TTWFpbk1lbnXSAA4APgGFB5+AYq8QXwT8A1cEUwYXA0UGFwNXAxwEJgRlA1cF9ANFAHAF
+xQRlAxwAHwNFBgoGFwNFBGUDVwNXAE8E/AYNBhYF0wOsBGUDVwNFBGUGFwBBBfUDvgRlAHEDHAQmA0UA
+TwBzA3gEZQBzA1cEZQAfAB8EZQYXAE8AcwXCA0UDVwOsBFMDRQOKBGUEBgBPA6wAcgRlBCYDVwNFBGUF
+xAYXBeIF5gYXAB8DrAQmBGUDRQQGA1cF8QNFAB8DrARTBFMF1QO+A1eBATaAroD8gQF6gKiBAXqAroCa
+gO6BAQKAroEBuICogA+BAYOBAQKAmoACgKiBAcSBAXqAqIEBAoCugK6ADIEBNoEBxoEBsoEBnYDIgQEC
+gK6AqIEBAoEBeoAHgQHCgM6BAQKAIYCagO6AqIAMgDCAuIEBAoAwgK6BAQKAAoACgQECgQF6gAyAMIEB
+eYCogK6AyID8gKiAvoEBAoDkgAyAyIAqgQECgO6AroCogQECgQGNgQF6gQGugQG+gQF6gAKAyIDugQEC
+gKiA5ICugQG6gKiAAoDIgPyA/IEBpIDOgK7SAA4APgGFCAGAYq8QYAT0A08ESwXCAz0FxAXFBMIFxwSn
+BckDYQB7A6wFzQQGA+IFYQSLAwoF0wXUBdUF1gQwAHMF2QN4BPwDigOUBd4D8QBPBF0F4gByBGUF5gEd
+ARUAzQQ+BXYDtgWpA3AFigSZBVMAQQXxA6QF9AX1AHABMgNXAHEF+gX5AB8FmAP+BG8DggUQBfsGAgDu
+BgoDxwQeBNAEtQQmBggDRQMcBg0FBwYPBTEFPwUdBA8GFAYWBFMGFwPVBWkEfQO+BN4GHYEBNYCtgPuB
+AXmAp4EBjYEBg4EBJIEBkIEBG4EBioCygBGAyIEBhoDkgNuBAVqBARGAmYEBnYEBo4EBpIEBi4DygDCB
+AaGAuIEBNoC+gMKBAa2A34AMgQEBgQGugCqBAQKBAb6Ah4CLgCOA94EBYYDNgQFygLeBAWiBARaBAVaA
+B4EBuoDHgQG4gQHCgA+ANICugCGBAX+BAbSAAoEBbYDjgQEHgL2BAT+BAZSBAZaALIEBxIDSgO2BASmB
+ASCA7oEBt4CogJqBAcaBATuBAZiBAUqBAU+BAUOA6IEBfoEBsoD8gQF6gNeBAV2BAQyAzoEBLoEBgtIA
+DgA+AYUIZIBirxBgCGUIZghnCGgIaQhqCGsIbAhtCG4IbwhwCHEIcghzCHQIdQh2CHcIeAh5CHoIewh8
+CH0Ifgh/CIAIgQiCCIMIhAiFCIYIhwiICIkIigiLCIwIjQiOCI8IkAiRCJIIkwiUCJUIlgiXCJgImQia
+CJsInAidCJ4InwigCKEIogijCKQIpQimCKcIqAipCKoIqwisCK0IrgivCLAIsQiyCLMItAi1CLYItwi4
+CLkIugi7CLwIvQi+CL8IwAjBCMIIwwjEgQHOgQHPgQHQgQHRgQHSgQHTgQHUgQHVgQHWgQHXgQHYgQHZ
+gQHagQHbgQHcgQHdgQHegQHfgQHggQHhgQHigQHjgQHkgQHlgQHmgQHngQHogQHpgQHqgQHrgQHsgQHt
+gQHugQHvgQHwgQHxgQHygQHzgQH0gQH1gQH2gQH3gQH4gQH5gQH6gQH7gQH8gQH9gQH+gQH/gQIAgQIB
+gQICgQIDgQIEgQIFgQIGgQIHgQIIgQIJgQIKgQILgQIMgQINgQIOgQIPgQIQgQIRgQISgQITgQIUgQIV
+gQIWgQIXgQIYgQIZgQIagQIbgQIcgQIdgQIegQIfgQIggQIhgQIigQIjgQIkgQIlgQImgQIngQIogQIp
+gQIqgQIrgQIsgQItXxAXTWVudSBJdGVtIChTaG93IENvbG9ycylfEBRNZW51IEl0ZW0gKFNob3cgQWxs
+KW8QGgBNAGUAbgB1ACAASQB0AGUAbQAgACgAUwBoAG8AdwAgAFMAcABlAGwAbABpAG4AZyAmAClfEBpN
+ZW51IEl0ZW0gKE5ld0FwcGxpY2F0aW9uKW8QEQBNAGUAbgB1ACAASQB0AGUAbQAgACgATwBwAGUAbiAm
+AClfEBJNZW51IEl0ZW0gKFdpbmRvdylfEBRNZW51IEl0ZW0gKFNlcnZpY2VzKV8QGE1lbnUgSXRlbSAo
+U21hcnQgUXVvdGVzKVtTZXBhcmF0b3ItNF8QD01lbnUgSXRlbSAoQ3V0KVlTZXBhcmF0b3JROF8QD1Rl
+eHQgRmllbGQgQ2VsbFtNZW51IChGaW5kKV8QD01lbnUgKFNlcnZpY2VzKVtNZW51IChWaWV3KVIxMFtB
+cHBsaWNhdGlvbl8QEE1lbnUgSXRlbSAoUmVkbylfEBxNZW51IEl0ZW0gKFNtYXJ0IENvcHkvUGFzdGUp
+XxASTWVudSBJdGVtIChGb3JtYXQpUzItMV8QEk1lbnUgSXRlbSAoU3BlZWNoKVtTZXBhcmF0b3ItMV8Q
+H01lbnUgSXRlbSAoSGlkZSBOZXdBcHBsaWNhdGlvbilbU2Nyb2xsIFZpZXdfEBZNZW51IEl0ZW0gKFNo
+b3cgRm9udHMpUTJdTWVudSAoRm9ybWF0KV8QEk1lbnUgKE9wZW4gUmVjZW50KVE1W1NlcGFyYXRvci01
+XxAgTWVudSBJdGVtIChBYm91dCBOZXdBcHBsaWNhdGlvbilcQ29udGVudCBWaWV3XxARTWVudSBJdGVt
+IChQYXN0ZSlfEBBNZW51IEl0ZW0gKEZpbGUpXxATUHVzaCBCdXR0b24gKFN0YXJ0KVtNZW51IChFZGl0
+KV8QGU1lbnUgSXRlbSAoU3Vic3RpdHV0aW9ucylfEBFWZXJ0aWNhbCBTY3JvbGxlcl8QE0hvcml6b250
+YWwgU2Nyb2xsZXJfEBdUZXh0IEZpZWxkIENlbGwgKFBvcnQ6KV8QHk1lbnUgSXRlbSAoQnJpbmcgQWxs
+IHRvIEZyb250KVMxLTFfEBlNZW51IEl0ZW0gKFN0b3AgU3BlYWtpbmcpXxAfU2hhcmVkIFVzZXIgRGVm
+YXVsdHMgQ29udHJvbGxlcl8QH01lbnUgSXRlbSAoTmV3QXBwbGljYXRpb24gSGVscClfEBBNZW51IEl0
+ZW0gKENvcHkpXxAXTWVudSBJdGVtIChTbWFydCBMaW5rcylfEBdNZW51IEl0ZW0gKEhpZGUgT3RoZXJz
+KV8QD1dpbmRvdyAoV2luZG93KV8QIE1lbnUgSXRlbSAoU3BlbGxpbmcgYW5kIEdyYW1tYXIpXxAdTWVu
+dSBJdGVtIChKdW1wIHRvIFNlbGVjdGlvbilfEBBNZW51IEl0ZW0gKEZpbmQpXxAQTWVudSBJdGVtIChF
+ZGl0KVpUZXh0IEZpZWxkWVRleHQgVmlld18QFU1lbnUgKE5ld0FwcGxpY2F0aW9uKV8QE1N0YXRpYyBU
+ZXh0IChQb3J0OilTMTIxUTdcRmlsZSdzIE93bmVyUTNvEB4ATQBlAG4AdQAgAEkAdABlAG0AIAAoAEMA
+dQBzAHQAbwBtAGkAegBlACAAVABvAG8AbABiAGEAciAmAClfEBZNZW51IEl0ZW0gKFNlbGVjdCBBbGwp
+XxAWTWVudSBJdGVtIChDbGVhciBNZW51KV8QJ01lbnUgSXRlbSAoQ2hlY2sgR3JhbW1hciBXaXRoIFNw
+ZWxsaW5nKV8QFU1lbnUgSXRlbSAoRmluZCBOZXh0KV8QGU1lbnUgSXRlbSAoRmluZCBQcmV2aW91cylf
+EBNCdXR0b24gQ2VsbCAoU3RhcnQpXxAQTWVudSBJdGVtIChWaWV3KVQxMTExXxAQTWVudSBJdGVtICha
+b29tKVE2XxASTWVudSBJdGVtIChEZWxldGUpXU1lbnUgKFdpbmRvdylbU2VwYXJhdG9yLTZbTWVudSAo
+RmlsZSlfEBRNZW51IChTdWJzdGl0dXRpb25zKVExXkFwcCBDb250cm9sbGVyXxAiTWVudSBJdGVtIChV
+c2UgU2VsZWN0aW9uIGZvciBGaW5kKV8QFE1lbnUgSXRlbSAoTWluaW1pemUpXxAQTWVudSBJdGVtIChV
+bmRvKVE5XxAYTWVudSBJdGVtIChTaG93IFRvb2xiYXIpW1NlcGFyYXRvci0yXxAXTWVudSBJdGVtIChP
+cGVuIFJlY2VudClfEBtNZW51IChTcGVsbGluZyBhbmQgR3JhbW1hcilYTWFpbk1lbnVfEBpNZW51IEl0
+ZW0gKFN0YXJ0IFNwZWFraW5nKV8QJ01lbnUgSXRlbSAoQ2hlY2sgU3BlbGxpbmcgV2hpbGUgVHlwaW5n
+KV8QGk1lbnUgSXRlbSAoQ2hlY2sgU3BlbGxpbmcpXU1lbnUgKFNwZWVjaClvEBEATQBlAG4AdQAgAEkA
+dABlAG0AIAAoAEYAaQBuAGQgJgApW1NlcGFyYXRvci0z0gAOAD4BhQkngGKg0gAOAD4BhQkqgGKg0gAO
+AD4BhQktgGKvEI4DTwRLAv4FwgLkBcQFxQTCBckDrANhAuAFzQSLAwoF0wLtBdQF1gLvAvcAcwXZA3gD
+igLsAuUC/AXeA/EEXQByAusEZQO2AM0BFQSZBD4FdgWpBYoFUwXxAEEDAAL2BfQAcANXAHEF+wMFA/4C
+4wLcAuIA7gS1BgoC8gYIAvAC9QMcBg0GDwU/AvgEUwLfAtkC6QMBBPQC+wM9At4FxwSnAHsEBgPiBWEC
+6gL5AwQF1QLmBDAC8wL0BPwC2gOkA5QC6ABPBeIBHQXmAtgC3QL6A3ADAwL9BfUBMgX5AvEF+gUQAB8F
+mAOCBG8C4QLuBgIE0AQeA8cC5wMCBCYDRQLbBQcFMQUdAv8EDwYUBhYGFwTeBWkEfQO+A9UGHYCtgPuB
+AVWBAXmA3oEBjYEBg4EBJIEBioDIgLKAzIEBhoEBEYCZgQGdgQELgQGjgQGLgQEVgQE6gDCBAaGAuIC+
+gQEGgOKBAU6BAa2A34EBAYAqgQEAgQECgM2AI4CLgQEWgPeBAWGBAXKBAWiBAVaBAbqAB4EBXIEBNIEB
+uIAPgK6AIYEBlIEBcYDjgNqAtoDWgCyBASCBAcSBASOBAbeBARqBATGAmoEBxoEBmIEBT4EBPoD8gMaA
+poD2gQFggQE1gQFJgKeAwYEBkIEBG4ARgOSA24EBWoD6gQFCgQFsgQGkgOeA8oEBKIEBLYEBNoCsgMeA
+woDxgAyBAa6Ah4EBvoCYgLyBAUeAt4EBZ4EBU4EBwoA0gQG0gQEfgQF/gQE/gAKBAW2AvYEBB4DRgQEQ
+gQGWgQEpgO2A0oDsgQFlgO6AqICxgQE7gQFKgQFDgQFZgOiBAX6BAbKBAXqBAS6BAV2BAQyAzoDXgQGC
+0gAOAD4BhQm+gGKvEI4JvwnACcEJwgnDCcQJxQnGCccJyAnJCcoJywnMCc0JzgnPCdAJ0QnSCdMJ1AnV
+CdYJ1wnYCdkJ2gnbCdwJ3QneCd8J4AnhCeIJ4wnkCeUJ5gnnCegJ6QnqCesJ7AntCe4J7wnwCfEJ8gnz
+CfQJ9Qn2CfcJ+An5CfoJ+wn8Cf0J/gn/CgAKAQoCCgMKBAoFCgYKBwoICgkKCgoLCgwKDQoOCg8KEAoR
+ChIKEwoUChUKFgoXChgKGQoaChsKHAodCh4KHwogCiEKIgojCiQKJQomCicKKAopCioKKwosCi0KLgov
+CjAKMQoyCjMKNAo1CjYKNwo4CjkKOgo7CjwKPQo+Cj8KQApBCkIKQwpECkUKRgpHCkgKSQpKCksKTIEC
+MoECM4ECNIECNYECNoECN4ECOIECOYECOoECO4ECPIECPYECPoECP4ECQIECQYECQoECQ4ECRIECRYEC
+RoECR4ECSIECSYECSoECS4ECTIECTYECToECT4ECUIECUYECUoECU4ECVIECVYECVoECV4ECWIECWYEC
+WoECW4ECXIECXYECXoECX4ECYIECYYECYoECY4ECZIECZYECZoECZ4ECaIECaYECaoECa4ECbIECbYEC
+boECb4ECcIECcYECcoECc4ECdIECdYECdoECd4ECeIECeYECeoECe4ECfIECfYECfoECf4ECgIECgYEC
+goECg4EChIEChYEChoECh4ECiIECiYECioECi4ECjIECjYECjoECj4ECkIECkYECkoECk4EClIEClYEC
+loECl4ECmIECmYECmoECm4ECnIECnYECnoECn4ECoIECoYECooECo4ECpIECpYECpoECp4ECqIECqYEC
+qoECq4ECrIECrYECroECr4ECsIECsYECsoECs4ECtIECtYECtoECt4ECuIECuYECuoECu4ECvIECvYEC
+voECvxCWEMwRAXAQOBCOEBMQgxEBXxCQENwQUBDjEIIQ1xEBXhEBKxDhEEoQlREBZREBhBEBfhEBWBBq
+EH0Q6BEBbRDfEM4QOhDLEQF7EOIQzRDDEQF6EQGAEQFiEAUQSREBhxDFEJEQ2BEBcxDeEQFpENoRAXcQ
+OREBeRDQEQGIEQEqEQFsEQFoEOkRAXwQyhEBJxEBZBDWEOQRAYkRAV0QZxDdEM8RAVsQ9REBdhAnEMER
+AVkQJRBIEFcQXBDHEQF4EQEoEHAT//////////0Q5hEBdREBahDTEQFuEIYQVhDxEQEsEQFyENIQTREB
+bxEBdBBTEQF/EQFcEQFjEH8RAYUQbxDgEQGGENkRAYEQTxDrEQFaEQGKEEsQfhDGEQFxEOcQ1RBOEO8Q
+iBDwEQGDEBgQUREBaxEBfRAXEFIRAYIRASkQ7BB8EB0Q0RDbEMkQ1BDEEI/SAA4APgBuCtuAa6DSAA4A
+PgGFCt6AYqDSAA4APgGFCuGAYqDSADcAOArjCuSiCuQAO15OU0lCT2JqZWN0RGF0YQAIABkAIgAnADEA
+OgA/AEQAUgBUAGYF8gX4BkMGSgZRBl8GcQaNBpsGpwazBsEGzAbaBvYHBAcXBykHQwdNB1oHXAdfB2IH
+ZQdoB2oHbQdvB3IHdQd4B3sHfQd/B4IHhQeIB4sHlAegB6IHpAeyB7sHxAfPB9QH4wfsB/8ICAgTCBUI
+GAgaCE8IXAhpCH8IjQiXCKUIvgjLCN0I8Qj7CQcJCQkLCQ0JDwkRCRYJGAkaCRwJHgkgCSIJJAk/CUYJ
+TwlaCXcJiQmUCZ0JqQm1CbcJuQm7Cb4JwAnCCcQJzQnPCdgJ2gncCd4J4AoBCgkKEAoaChwKHgogCiIK
+JQomCigKKgpCCmsKdwqLCpYKoAquCrsKzwrlCvEK+gr8Cv4LAAsCCwQLCQsKCwwLDgsPCyALJwsuCzcL
+OQtCC0QLRwtUC10LYgtpC20LgguKC5cLowuxC7MLtQu3C7kLuwvCC9gL5QvtC+8L8Qv0C/0MAgwXDBkM
+GwwdDB8MKQw2DDgMOwxEDE0MXwxsDHUMgAyMDJYMnQypDMoMzAzODNAM0gzTDNUM1wzvDRANFQ0XDRkN
+Gw0dDR8NIQ0nDTwNPg1ADUINRA1RDV4NYA1sDYENgw2FDYcNiQ2cDb0Nvw3BDcMNxQ3IDckNyw3NDeYO
+Fw4tDkIOUQ5jDnUOgw6IDooOjA6ODpAOkg6UDpYOmA6aDp8OqA6uDrcOwA7NDtYO4Q7qDxsPJw8wDzwP
+Sg9YD1oPXA9eD2APYw9lD2gPag9sD24PcA9yD3sPfQ+ED4YPiA+KD7sPxA/OD9gP4g/kD+YP6A/qD+wP
+7g/xD/MP9Q/3D/kP+xAEEAYQCRALEDwQThBYEGUQbxB4EIMQhRCHEIkQixCOEJAQkhCUEJYQmBCaEKUQ
+uhDEEM8Q1xDpEOsQ7RDvEPgQ+hEPESIRMBE6ETwRPhFAEUIRVxFkEW0RfxGBEYMRhRGHEYkRkhGcEZ4U
+ABQJFBAUIhQrFC0UMhQ0FDYUQxRLFE0UUhRUFFYUWxRdFF8UchR/FIoUlhSYFJoUoxSlFOgU6hTsFO4U
+8BTyFPQU9hT4FPoU/BT+FQAVAhUEFQYVCBUKFQwVDhUQFRIVFBUWFRgVGhUcFR4VIBUiFSQVJhUoFTEV
+PBU+FUcVUBVVFV8VaBVqFXMVfBV+FYcVkBWSFZsVpBWmFa8VuBW6FcMVzBXOFdcV4BXiFesV9BX2Ff8W
+CBYKFhMWHBYeFicWMBYyFjsWRBZGFk8WWBZaFmMWbBZuFncWgBaCFosWlBaWFp8WqBaqFrMWvBa+FscW
+0BbSFtsW5BbmFu8W+Bb6FwMXDBcOFxcXIBciFysXNBc2Fz8XSBdKF1MXXBdeF2cXcBdyF3sXhBeGF48X
+mBeaF6MXrBeuF7cXwBfCF8sX1BfZF+EX6hfvGAAYAhgLGA0YEBgZGB4YKxg4GDoYPxhBGEMYSBhKGEwY
+WRhbGF0YZhhoGKsYrRivGLEYsxi1GLcYuRi7GL0YvxjBGMMYxRjHGMkYyxjNGM8Y0RjTGNUY1xjZGNsY
+3RjfGOEY4xjlGOcY6RjrGPwY/hkAGQIZFhkfGSYZNRk+GUYZSBlaGWMZahl4GX8ZiBmRGa0ZwhnLGc0Z
+0BnSGdsZ4BnpGe4aDxoXGjEaRBpbGnAagxqFGogaihqMGo4akBqSGpQaoRqjGqgaqhqsGrEasxq1Gsoa
+zBrOGtAa0hrwGwUbBxsJGwsbDRshGy4bMBs1GzcbORs+G0AbQhtOG1sbYRtjG2obcxt4G48bnBukG60b
+uBu/G9Yb4xvtG/ob/Bv+HAYcDxwUHB0cJhwxHFYcXxxoHHIcdBx2HHgcehyDHIUchxyJHJIcqhy3HMAc
+yxzWHQMdDh0QHRIdFB0WHRgdGh0cHSUdLh1JHWIdax10HYEdjB2VHZwdtR3AHdMd3B3hHfQd/R4EHhEe
+Fx4gHiIegR6DHoUehx6JHosejR6PHpEekx6VHpcemR6bHp0enx6hHqMepR6nHqoerR6wHrMeth65Hrwe
+vx7CHsUeyB7LHs4e0R7UHtce2h7dHuAe4x7mHuke7B7vHvIe9R74HwUfDh8WHxgfGh8cH0EfSR9dH2gf
+dh+AH40flB+aH5wfnh+jH6Ufqh+sH64fsB+9H8kfzB/PH9If5R/nH/QgAyAFIAcgCSARICMgLCAxIEQg
+USBTIFUgVyBqIHMgeCCDIJ4gpyCuIMYg1SDiIOQg5iDoIQkhCyENIQ8hESETIRUhIiElISghKyE2ITgh
+RiFTIVUhVyFZIXohfCF+IYAhgiGEIYYhlyGaIZ0hoCGjIawhxSHSIdQh1iHYIfkh+yH9IgIiBCIGIggi
+CiIbIh0iLyI8Ij4iQCJCImMiZSJnImkiayJtIm8ifCJ/IoIihSKbIp0ipyK0IrYiuCK6Itsi3SLfIuEi
+4yLlIuci+CL7Iv4jASMEIw8jJyM0IzYjOCM6I18jaSNrI20jbyNxI3MjdSN3I4UjhyOWI6MjpSOnI6kj
+yiPMI84j0CPSI9Qj1iPjI+Yj6SPsJAAkAiQiJC8kMSQzJDUkViRYJFokXCReJGAkYiRvJHIkdSR4JIYk
+lCShJKMkpSSnJMgkyiTMJM4k0CTSJNQk6iTsJPclBCUGJQglCiUrJS0lLyUxJTMlNSU3JUYlVSViJWQl
+ZiVoJYUlhyWJJYsljSWPJZEloyW8Jc0l2yXdJd8l4SXjJgAmAiYEJgYmCCYKJgwmIyZDJlAmUiZUJlYm
+dyZ5JnsmfSZ/JoEmgyaQJpMmliaZJsAm4ibvJvEm8yb1JxYnGCcaJx8nIScjJyUnJyc0JzYnTCdZJ1sn
+XSdfJ4AngieEJ4YniCeKJ4wnnSegJ6InpSeoJ60nuifHJ8knyyfNJ+4n8CfyJ/Qn9if4J/ooECgSKBgo
+JSgnKCkoKyhMKE4oUChSKFQoVihYKG0ofyiMKI4okCiSKLMotSi3KLkouyi9KL8ozCjPKNIo1SjyKPQp
+BikTKRUpGCkbKTwpPilBKUQpRilIKUspWClbKV4pYSlnKWkpcCl9KX8pgimFKaYpqCmrKa4psCmyKbUp
+wCnCKc0p2incKd8p4ioDKgUqCCoLKg0qDyoRKiAqIioxKj4qQCpDKkYqZyppKmwqbypxKnMqdip7Kn0q
+gyqQKpIqlSqYKr0qvyrCKsUqxyrJKssq1yrZKvkrBisIKwsrDisvKzErNCs3KzkrOys+K0IrRCtJK1Yr
+WCtbK14rfyuBK4QrhiuIK4orjSuUK5wrqSurK64rsSvWK9gr2yveK+Ar4ivkK/Er8ywXLCQsJiwpLCws
+TSxPLFIsVSxXLFksWyxoLGoscSx+LIAsgyyGLKssrSywLLIstCy2LLgswyzdLO4s8SzzLPUs+C0QLRkt
+IC03LUQtRi1JLUwtbS1vLXItdS13LXktfC2JLYwtjy2SLZ4toC24LcktzC3OLdEt1C3dLd8t4i3wLfou
+By4JLgwuDy4wLjIuNS43LjkuOy49LlsudC6BLoMuhi6JLqourC6vLrIutC62LrguvC6+Lssu3C7fLuEu
+5C7nLvkvBi8ILwsvDi8vLzEvNC83LzkvOy89L0YvSC9eL2svbS9wL3MvlC+WL5kvnC+eL6Avoy+oL6ov
+sC/BL8Mvxi/IL8sv1i/jL+Uv6C/rMAwwDjARMBMwFTAXMBkwJTA+ME8wUjBVMFgwWzBkMGYwaDBxMH4w
+gDCDMIYwpzCpMKwwrjCwMLIwtDDSMPMxADECMQUxCDEpMSsxLjExMTMxNTE3MT0xPzFNMV4xYTFjMWYx
+aTFxMX4xgDGDMYYxpzGpMawxrzGxMbMxtjG7Mb0xwzHQMdIx1THYMfkx+zH+MgEyAzIFMgcyDDIOMhwy
+OTJDMk0ybDJvMnIydTJ4MnoyfTKGMpkynDKdMqYyrTLIMtUy8DL2MwozEzMaMzIzOzM9M/40ATQDNAU0
+CDQKNA00EDQTNBY0GTQcNB40IDQiNCU0KDQqNC00LzQxNDQ0NzQ6ND00PzRBNEQ0RjRINEs0TTRQNFI0
+VDRXNFo0XDRfNGE0ZDRmNGk0azRuNHA0cjR0NHc0eTR8NH80gTSENIc0ijSMNI40kDSTNJY0mTScNJ80
+oTSkNKY0qDSrNK00sDSyNLQ0tzS6NLw0vzTBNMM0xjTJNMw0zzTSNNU01zTaNNw03zTiNOU06DTrNO00
+7zTyNRs1JTUnNSk1LDUuNTA1MjU1NTg1STVMNU81UjVVNWQ1czV8NX41lTWXNZo1nTWgNaM1pjWoNas1
+rTWwNbI12zXpNfY1+DX6Nfs1/TX+NgA2AjYENiU2JzYqNi02LzYxNjM2TDZONnc2eTZ7Nnw2fjZ/NoE2
+gzaFNq42sDazNrY2uDa6Nrw2vjbBNso22zbeNuE25DbnNvA28jbzNwU3DjcTNzw3PjdAN0E3QzdEN0Y3
+SDdKN3M3dTd3N3g3ejd7N303fzeBN443tze5N7s3vTe/N8E3wzfGN8k30jfUN9034DfiN+U35zgQOBI4
+FDgVOBc4GDgaOBw4HjgtODI4Ozg9OEg4SzhOOFE4VDhWOHs4fTiAOIM4hTiHOIk4kzi4OLo4vTjAOMI4
+xDjGONQ4+Tj7OP45ATkDOQU5BzkJOSI5JDkpOTI5NDk5OTs5PTlmOWg5azluOXA5cjl0OXc5ejmBOYo5
+jDmROZQ5lzm4Obo5vTm/OcE5wznGOdE5+jn8Of45/zoBOgI6BDoGOgg6MTozOjU6ODo6Ojw6PjpBOkQ6
+SzpUOlY6WzpdOl86ZDptOm86cjp0OoA6iTqLOo46kDqpOtI61DrWOtc62TraOtw63jrhOwo7DDsOOxE7
+EzsVOxc7GjsdOyI7KzstO0Q7RztJO0w7TztSO1U7VztZO1w7XjthO4o7jDuOO5E7kzuVO5c7mTucO8U7
+xzvJO8o7zDvNO8870TvTO9g74TvjO/48ATwEPAc8CjwNPBA8EzwWPBk8HDwfPCI8JTxOPFA8UjxTPFU8
+VjxYPFo8XTyGPIg8ijyNPI88kTyTPJY8mTzCPMQ8xjzJPMs8zTzPPNI81TzsPPU89z0APQI9BT0IPQs9
+ND02PTg9Oz09PT89QT1EPUc9VT1ePWA9Zz1pPWw9bz2YPZo9nT2gPaI9pD2mPak9rD3VPdc92T3cPd49
+4D3iPeU96D4RPhM+FT4YPho+HD4ePiE+JD4uPjc+OT5IPks+Tj5RPlQ+Vz5aPl0+aT5yPnQ/NT84Pzo/
+PD8/P0E/RD9GP0g/Sj9NP08/Uj9UP1Y/WT9cP14/YD9iP2U/aD9qP20/bz9xP3M/dj95P3w/fz+BP4Q/
+hj+IP4s/jj+QP5M/lT+YP5o/nD+eP6A/oj+kP6Y/qT+rP60/sD+yP7Q/tz+6P7w/vj/BP8M/xT/HP8k/
+yz/NP9A/0j/UP9Y/2D/bP90/3z/hP+Q/5z/qP+0/8D/zP/U/9z/5P/w//kAAQAJABUAHQAlAC0ANQA9A
+EkAUQBZAH0AhQORA50DpQOtA7kDwQPNA9kD5QPxA/0ECQQRBBkEIQQtBDUEPQRJBFUEXQRpBHUEgQSNB
+JUEnQSpBLEEvQTFBM0E2QThBOkE9QUBBQkFFQUhBSkFMQU5BUEFTQVVBWEFaQV1BYEFjQWVBaEFqQW1B
+cEFyQXRBdkF4QXtBfkGAQYNBhUGIQYpBjUGQQZNBlUGYQZpBnEGfQaJBpEGnQalBq0GuQbFBtEG3QbpB
+vUG/QcJBxUHHQcpBzEHPQdJB1EHXQdpB40HlQqhCq0KuQrFCtEK3QrpCvULAQsNCxkLJQsxCz0LSQtVC
+2ELbQt5C4ULkQudC6kLtQvBC80L2QvlC/EL/QwJDBUMIQwtDDkMRQxRDF0MaQx1DIEMjQyZDKUMsQy9D
+MkM1QzhDO0M+Q0FDRENHQ0pDTUNQQ1NDVkNZQ1xDX0NiQ2VDaENrQ25DcUN0Q3dDekN9Q4BDg0OGQ4lD
+jEOPQ5JDlUOYQ5tDnkOhQ6RDp0OqQ61DsEOzQ7ZDuUO8Q79DwkPFQ8hD4kP5RDBETURyRIdEnkS5RMVE
+10ThRONE9UUBRRNFH0UiRS5FQUVgRXVFeUWORZpFvEXIReFF40XxRgZGCEYURjdGREZYRmtGgUaNRqlG
+vUbTRu1HDkcSRy5HUEdyR4VHn0e5R8tH7kgOSCFINEg/SElIYUh3SHtIfUiKSIxIy0jkSP1JJ0k/SVtJ
+cUmESYlJnEmeSbNJwUnNSdlJ8EnySgFKJko9SlBKUkptSnlKk0qxSrpK10sBSx5LLEtRS11LZktoS2lL
+ckt0S3VLfkuATJ9MoUyjTKZMqUyrTK5MsUy0TLdMuUy7TL1MwEzDTMVMyEzLTM5M0UzUTNdM2UzcTN5M
+4EzjTOVM6EzrTO1M8EzyTPVM+Ez6TPxM/k0BTQNNBk0JTQxND00STRRNF00aTR1NH00hTSNNJk0pTStN
+LU0vTTFNM002TTlNPE0/TUJNRU1HTUpNTU1QTVNNVU1XTVlNW01eTWFNZE1mTWhNa01uTXBNck10TXdN
+eU18TX9Ngk2ETYZNiU2MTY9NkU2TTZVNl02ZTZxNnk2hTaNNpU2oTapNrU2wTbNNtU24TbtNvk3BTcNN
+xk3ITctNzU3QTdNN1k3YTdpN3E3fTeFN403lTehN603uTfFN8032TflN/E3/TgJOBU4HTglODE4VThdP
+Nk85TzxPP09CT0VPSE9LT05PUU9UT1dPWk9dT2BPY09mT2lPbE9vT3JPdU94T3tPfk+BT4RPh0+KT41P
+kE+TT5ZPmU+cT59Pok+lT6hPq0+uT7FPtE+3T7pPvU/AT8NPxk/JT8xPz0/ST9VP2E/bT95P4U/kT+dP
+6k/tT/BP80/2T/lP/E//UAJQBVAIUAtQDlARUBRQF1AaUB1QIFAjUCZQKVAsUC9QMlA1UDhQO1A+UEFQ
+RFBHUEpQTVBQUFNQVlBZUFxQX1BiUGVQaFBrUG5QcVB0UHdQelB9UIBQg1CGUIlQjFCPUJJQlVCYUJtQ
+nlChUKRQp1CqUK1QsFCzULZQuVC8UL9QwlDFUMhQy1DOUNFQ1FDXUNpQ3VDgUOJQ5FDnUOlQ61DtUO9Q
+8lD0UPZQ+FD6UPxQ/lEBUQRRBlEIUQpRDVEQURNRFlEYURpRHFEfUSFRI1ElUSdRKlEsUS5RMFEzUTZR
+OVE7UT1RQFFCUURRRlFJUUtRTlFQUVNRVVFYUVpRXVFgUWNRZlFoUWtRbVFwUXNRdVF3UXpRfVF/UYFR
+g1GGUYhRi1GNUY9RklGUUZZRmFGaUZxRn1GiUaRRrVGvUbJRtVG3UbpRvFG+UcBRw1HGUchRylHNUdBR
+0lHVUdhR21HdUeBR4lHkUedR6VHsUe5R8FHzUfZR+FH6UfxR/1IBUgNSBVIHUglSC1IOUhBSElIVUhhS
+GlIcUh9SIlIkUiZSKFIqUixSLlIwUjJSNFI9Uj9SQFJJUktSTFJVUldSWFJhUmYAAAAAAAACAgAAAAAA
+AArlAAAAAAAAAAAAAAAAAABSdQ</bytes>
+ </object>
+ </data>
+</archive>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.EchoServer</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
--- /dev/null
+First, build and run the EchoServer.
+You can set it to run on a specific port, or allow to pick an available port.
+Now start the echo server. It will output the port it started on.
+
+You can now use telnet to connect to the echo server and test it.
+Using the Terminal (/Applications/Utilities/Terminal.app)
+type in the following:
+telnet localhost [port]
+
+where "[port]" is replaced by the port the Echo server is running on.
+
+Type in anything you want, and hit return.
+Whatever you typed in will be displayed in the Echo Server, and echoed back to you.
+
+To end your telnet session hit Ctrl-], and type "quit".
\ No newline at end of file
--- /dev/null
+//
+// main.m
+// EchoServer
+//
+// Created by Robbie Hanson on 7/10/08.
+// Copyright __MyCompanyName__ 2008. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, char *argv[])
+{
+ return NSApplicationMain(argc, (const char **) argv);
+}
--- /dev/null
+The last version of AsyncSocket from Dustin Voss was version 4.3 released on 2005-11-23. This document lists the bug fixes provided by Deusty Designs, prior to the creation of the Google Code project.
+
+CHANGES IN VERSION 4.3.1:
+
+Bugfix:
+ If user called acceptOnPort:0, then the OS would automatically pick a port for you.
+ This is what is supposed to happen, except that it would pick a different port for IPv4 and IPv6
+ Added code to make sure both protocols are listening on the same port
+
+Added comments in many places
+
+Altered bits of code to match Apple's coding style guidelines
+
+Renamed method "attachAcceptSockets" to "attachSocketsToRunLoop:error:"
+
+
+
+
+CHANGES IN VERSION 4.3.2
+
+Removed polling - it's not needed
+
+Added another delegate method: onSocket:didReadPartialDataOfLength:tag:
+Often, when using the AsyncSocket class, it was important to display a progress bar to the user.
+This was possible using Timers, and calling progressOfRead, but it wasn't the easiest solution.
+This delegate method will allow for automatic notification when using readToLength: or readToData:
+
+
+
+
+CHANGES IN VERSION 4.3.3
+
+Bugfix:
+ The methods connectedHost, connectedPort, localHost, and localPort all assumed IPv4 connection.
+ In other words they all assumed theSocket was valid, causing a crash when the OS connected via IPv6.
+ Updated all methods to properly check either theSocket or theSocket6.
+
+Bugfix:
+ In the doStreamOpen method, there was an assumption that IPv4 was used and thus a valid theSocket variable.
+ This was not always the case, causing this to fail:
+ CFSocketCopyPeerAddress(theSocket)
+ Fixed the problem by simply using the connectedHost and connectedPort methods.
+
+Tweak:
+ Added safety check in addressPort: method:
+ if (cfaddr == NULL) return 0;
+
+Tweak:
+ The kCFStreamPropertyShouldCloseNativeSocket was previously getting set in the configureStreamsAndReturnError: method.
+ This may have been OK, but it would have caused a problem if the following sequence occurred:
+ A socket was accepted and doAcceptWithSocket: method was called,
+ This method then encoutered an error while attaching the streams to the run loop.
+ If this sequence happened, the BSD socket would not have been closed.
+ So I moved this into the createStreams... methods.
+
+
+
+
+CHANGES IN VERSION 4.3.4
+
+Bugfix:
+ In doReadTimeout: the code properly calls endCurrentWrite and then closes with an error.
+ In doWriteTimeout: the code was calling completeCurrentWrite and then closes with an error.
+ This resulted in the delegate being told that his write completed (when it didn't)
+ immediately prior to disconnecting with an error.
+
+
+
+
+CHANGES IN VERSION 4.3.5
+
+Added support for accepting on IPv6 address.
+
+Bugfix:
+ In acceptOnAddress, changed dataWithBytesNoCopy to dataWithBytes.
+ This was needed because the bytes were about to go out of scope.
+
+Bugfix:
+ Added return statement to doStreamOpen after closewithError call.
+ This was needed or else the didConnect delegate could potentially get called
+ immediately after willCloseWithError and didClose.
+
+Bugfix:
+ We were receiving several reports of crashes in AsyncSocket.
+ The problem seemed to be that, in specific circumstances,
+ the readStream callback and/or writeStream callback would be invoked AFTER
+ the readStream and writeStream were closed.
+ This isn't supposed to happen, however we did find evidence that it was an issue several years ago.
+ It was assumed that the problem has since been fixed.
+ Perhaps the problem still exists, but only in very rare cases which we just happened to be encountering.
+ In any case, we used the same precautions that were used previously.
+ In the close methods, we specifically unregister for callbacks now.
+
+
+
+
+There have been MANY more bug fixes and changes. Please consult the google code changes list:
+http://code.google.com/p/cocoaasyncsocket/source/list
\ No newline at end of file