├── .github └── FUNDING.yml ├── .gitignore ├── AnyBar.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ ├── AnyBar.xccheckout │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── prokopov.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── WorkspaceSettings.xcsettings └── xcuserdata │ └── prokopov.xcuserdatad │ └── xcschemes │ ├── AnyBar.xcscheme │ └── xcschememanagement.plist ├── AnyBar ├── AnyBar.sdef ├── AnyBarApp.h ├── AnyBarApp.m ├── AppDelegate.h ├── AppDelegate.m ├── Base.lproj │ └── MainMenu.xib ├── GCDAsyncSocket.h ├── GCDAsyncSocket.m ├── GCDAsyncUdpSocket.h ├── GCDAsyncUdpSocket.m ├── Images.xcassets │ └── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon_128x128.png │ │ ├── icon_128x128@2x.png │ │ ├── icon_512x512.png │ │ └── icon_512x512@2x.png ├── Info.plist ├── Resources │ ├── black@2x.png │ ├── blue@2x.png │ ├── cyan@2x.png │ ├── exclamation@2x.png │ ├── filled@2x.png │ ├── green@2x.png │ ├── hollow@2x.png │ ├── orange@2x.png │ ├── purple@2x.png │ ├── question@2x.png │ ├── red@2x.png │ ├── white@2x.png │ └── yellow@2x.png └── main.m ├── CHANGELOG.md ├── LICENSE ├── README.md ├── Tests ├── Makefile ├── control-anybar.applescript ├── osa-test.sh └── test-osa-script.applescript ├── anybar_patreon.png └── screenshot.png /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [tonsky] 2 | patreon: tonsky 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | xcuserdata/ 2 | build 3 | *.zip -------------------------------------------------------------------------------- /AnyBar.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 7648BAC31AA5A50B003DA28F /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = C5AB32C41A8F9091002258B6 /* MainMenu.xib */; }; 11 | C03A3D3E1AA78DAD0030B668 /* AnyBar.sdef in Resources */ = {isa = PBXBuildFile; fileRef = C03A3D3D1AA78DAD0030B668 /* AnyBar.sdef */; }; 12 | C03A3D431AA7A6540030B668 /* AnyBarApp.m in Sources */ = {isa = PBXBuildFile; fileRef = C03A3D421AA7A6540030B668 /* AnyBarApp.m */; }; 13 | C504CCFB25DEE28400DDEE71 /* filled@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C504CCF925DEE28400DDEE71 /* filled@2x.png */; }; 14 | C504CCFC25DEE28400DDEE71 /* hollow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C504CCFA25DEE28400DDEE71 /* hollow@2x.png */; }; 15 | C5AB32BF1A8F9091002258B6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C5AB32BE1A8F9091002258B6 /* AppDelegate.m */; }; 16 | C5AB32C11A8F9091002258B6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C5AB32C01A8F9091002258B6 /* main.m */; }; 17 | C5AB32C31A8F9091002258B6 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C5AB32C21A8F9091002258B6 /* Images.xcassets */; }; 18 | C5AB32E71A8F9B4E002258B6 /* GCDAsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = C5AB32E41A8F9B4E002258B6 /* GCDAsyncSocket.m */; }; 19 | C5AB32E81A8F9B4E002258B6 /* GCDAsyncUdpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = C5AB32E61A8F9B4E002258B6 /* GCDAsyncUdpSocket.m */; }; 20 | C5E0271E1AA6435E0032F2E9 /* black@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C5E027111AA6435E0032F2E9 /* black@2x.png */; }; 21 | C5E0271F1AA6435E0032F2E9 /* blue@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C5E027121AA6435E0032F2E9 /* blue@2x.png */; }; 22 | C5E027201AA6435E0032F2E9 /* cyan@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C5E027131AA6435E0032F2E9 /* cyan@2x.png */; }; 23 | C5E027211AA6435E0032F2E9 /* green@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C5E027141AA6435E0032F2E9 /* green@2x.png */; }; 24 | C5E027221AA6435E0032F2E9 /* orange@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C5E027151AA6435E0032F2E9 /* orange@2x.png */; }; 25 | C5E027231AA6435E0032F2E9 /* purple@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C5E027161AA6435E0032F2E9 /* purple@2x.png */; }; 26 | C5E027241AA6435E0032F2E9 /* red@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C5E027171AA6435E0032F2E9 /* red@2x.png */; }; 27 | C5E027281AA6435E0032F2E9 /* white@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C5E0271B1AA6435E0032F2E9 /* white@2x.png */; }; 28 | C5E027291AA6435E0032F2E9 /* yellow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C5E0271C1AA6435E0032F2E9 /* yellow@2x.png */; }; 29 | C5E0272F1AA64BD90032F2E9 /* exclamation@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C5E0272C1AA64BD90032F2E9 /* exclamation@2x.png */; }; 30 | C5E027311AA64BD90032F2E9 /* question@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C5E0272E1AA64BD90032F2E9 /* question@2x.png */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | C03A3D3D1AA78DAD0030B668 /* AnyBar.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = AnyBar.sdef; sourceTree = ""; }; 35 | C03A3D411AA7A6540030B668 /* AnyBarApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnyBarApp.h; sourceTree = ""; }; 36 | C03A3D421AA7A6540030B668 /* AnyBarApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AnyBarApp.m; sourceTree = ""; }; 37 | C504CCF925DEE28400DDEE71 /* filled@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "filled@2x.png"; sourceTree = ""; }; 38 | C504CCFA25DEE28400DDEE71 /* hollow@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "hollow@2x.png"; sourceTree = ""; }; 39 | C511DFFB1AA4E9CF00DEE15F /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 40 | C5AB32B81A8F9091002258B6 /* AnyBar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AnyBar.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | C5AB32BC1A8F9091002258B6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | C5AB32BD1A8F9091002258B6 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 43 | C5AB32BE1A8F9091002258B6 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 44 | C5AB32C01A8F9091002258B6 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 45 | C5AB32C21A8F9091002258B6 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 46 | C5AB32C51A8F9091002258B6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 47 | C5AB32E31A8F9B4E002258B6 /* GCDAsyncSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDAsyncSocket.h; sourceTree = ""; }; 48 | C5AB32E41A8F9B4E002258B6 /* GCDAsyncSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDAsyncSocket.m; sourceTree = ""; }; 49 | C5AB32E51A8F9B4E002258B6 /* GCDAsyncUdpSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDAsyncUdpSocket.h; sourceTree = ""; }; 50 | C5AB32E61A8F9B4E002258B6 /* GCDAsyncUdpSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDAsyncUdpSocket.m; sourceTree = ""; }; 51 | C5E027111AA6435E0032F2E9 /* black@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "black@2x.png"; sourceTree = ""; }; 52 | C5E027121AA6435E0032F2E9 /* blue@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "blue@2x.png"; sourceTree = ""; }; 53 | C5E027131AA6435E0032F2E9 /* cyan@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "cyan@2x.png"; sourceTree = ""; }; 54 | C5E027141AA6435E0032F2E9 /* green@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "green@2x.png"; sourceTree = ""; }; 55 | C5E027151AA6435E0032F2E9 /* orange@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "orange@2x.png"; sourceTree = ""; }; 56 | C5E027161AA6435E0032F2E9 /* purple@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "purple@2x.png"; sourceTree = ""; }; 57 | C5E027171AA6435E0032F2E9 /* red@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "red@2x.png"; sourceTree = ""; }; 58 | C5E0271B1AA6435E0032F2E9 /* white@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "white@2x.png"; sourceTree = ""; }; 59 | C5E0271C1AA6435E0032F2E9 /* yellow@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "yellow@2x.png"; sourceTree = ""; }; 60 | C5E0272C1AA64BD90032F2E9 /* exclamation@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "exclamation@2x.png"; sourceTree = ""; }; 61 | C5E0272E1AA64BD90032F2E9 /* question@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "question@2x.png"; sourceTree = ""; }; 62 | /* End PBXFileReference section */ 63 | 64 | /* Begin PBXFrameworksBuildPhase section */ 65 | C5AB32B51A8F9091002258B6 /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | 7648BAAC1AA5A3C0003DA28F /* Resources */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | C504CCF925DEE28400DDEE71 /* filled@2x.png */, 79 | C504CCFA25DEE28400DDEE71 /* hollow@2x.png */, 80 | C5E0272C1AA64BD90032F2E9 /* exclamation@2x.png */, 81 | C5E0272E1AA64BD90032F2E9 /* question@2x.png */, 82 | C5E027111AA6435E0032F2E9 /* black@2x.png */, 83 | C5E027121AA6435E0032F2E9 /* blue@2x.png */, 84 | C5E027131AA6435E0032F2E9 /* cyan@2x.png */, 85 | C5E027141AA6435E0032F2E9 /* green@2x.png */, 86 | C5E027151AA6435E0032F2E9 /* orange@2x.png */, 87 | C5E027161AA6435E0032F2E9 /* purple@2x.png */, 88 | C5E027171AA6435E0032F2E9 /* red@2x.png */, 89 | C5E0271B1AA6435E0032F2E9 /* white@2x.png */, 90 | C5E0271C1AA6435E0032F2E9 /* yellow@2x.png */, 91 | ); 92 | path = Resources; 93 | sourceTree = ""; 94 | }; 95 | C5AB32AF1A8F9091002258B6 = { 96 | isa = PBXGroup; 97 | children = ( 98 | C5AB32BA1A8F9091002258B6 /* AnyBar */, 99 | C5AB32B91A8F9091002258B6 /* Products */, 100 | C511DFFB1AA4E9CF00DEE15F /* README.md */, 101 | ); 102 | sourceTree = ""; 103 | }; 104 | C5AB32B91A8F9091002258B6 /* Products */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | C5AB32B81A8F9091002258B6 /* AnyBar.app */, 108 | ); 109 | name = Products; 110 | sourceTree = ""; 111 | }; 112 | C5AB32BA1A8F9091002258B6 /* AnyBar */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | C5AB32E31A8F9B4E002258B6 /* GCDAsyncSocket.h */, 116 | C5AB32E41A8F9B4E002258B6 /* GCDAsyncSocket.m */, 117 | C5AB32E51A8F9B4E002258B6 /* GCDAsyncUdpSocket.h */, 118 | C5AB32E61A8F9B4E002258B6 /* GCDAsyncUdpSocket.m */, 119 | C5AB32BD1A8F9091002258B6 /* AppDelegate.h */, 120 | C5AB32BE1A8F9091002258B6 /* AppDelegate.m */, 121 | C03A3D411AA7A6540030B668 /* AnyBarApp.h */, 122 | C03A3D421AA7A6540030B668 /* AnyBarApp.m */, 123 | C5AB32C21A8F9091002258B6 /* Images.xcassets */, 124 | C5AB32C41A8F9091002258B6 /* MainMenu.xib */, 125 | 7648BAAC1AA5A3C0003DA28F /* Resources */, 126 | C5AB32BB1A8F9091002258B6 /* Supporting Files */, 127 | ); 128 | path = AnyBar; 129 | sourceTree = ""; 130 | }; 131 | C5AB32BB1A8F9091002258B6 /* Supporting Files */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | C03A3D3D1AA78DAD0030B668 /* AnyBar.sdef */, 135 | C5AB32BC1A8F9091002258B6 /* Info.plist */, 136 | C5AB32C01A8F9091002258B6 /* main.m */, 137 | ); 138 | name = "Supporting Files"; 139 | sourceTree = ""; 140 | }; 141 | /* End PBXGroup section */ 142 | 143 | /* Begin PBXNativeTarget section */ 144 | C5AB32B71A8F9091002258B6 /* AnyBar */ = { 145 | isa = PBXNativeTarget; 146 | buildConfigurationList = C5AB32D51A8F9091002258B6 /* Build configuration list for PBXNativeTarget "AnyBar" */; 147 | buildPhases = ( 148 | C5AB32B41A8F9091002258B6 /* Sources */, 149 | C5AB32B51A8F9091002258B6 /* Frameworks */, 150 | C5AB32B61A8F9091002258B6 /* Resources */, 151 | ); 152 | buildRules = ( 153 | ); 154 | dependencies = ( 155 | ); 156 | name = AnyBar; 157 | productName = AnyBar; 158 | productReference = C5AB32B81A8F9091002258B6 /* AnyBar.app */; 159 | productType = "com.apple.product-type.application"; 160 | }; 161 | /* End PBXNativeTarget section */ 162 | 163 | /* Begin PBXProject section */ 164 | C5AB32B01A8F9091002258B6 /* Project object */ = { 165 | isa = PBXProject; 166 | attributes = { 167 | LastUpgradeCheck = 0810; 168 | ORGANIZATIONNAME = "Nikita Prokopov"; 169 | TargetAttributes = { 170 | C5AB32B71A8F9091002258B6 = { 171 | CreatedOnToolsVersion = 6.1.1; 172 | }; 173 | }; 174 | }; 175 | buildConfigurationList = C5AB32B31A8F9091002258B6 /* Build configuration list for PBXProject "AnyBar" */; 176 | compatibilityVersion = "Xcode 3.2"; 177 | developmentRegion = English; 178 | hasScannedForEncodings = 0; 179 | knownRegions = ( 180 | English, 181 | en, 182 | Base, 183 | ); 184 | mainGroup = C5AB32AF1A8F9091002258B6; 185 | productRefGroup = C5AB32B91A8F9091002258B6 /* Products */; 186 | projectDirPath = ""; 187 | projectRoot = ""; 188 | targets = ( 189 | C5AB32B71A8F9091002258B6 /* AnyBar */, 190 | ); 191 | }; 192 | /* End PBXProject section */ 193 | 194 | /* Begin PBXResourcesBuildPhase section */ 195 | C5AB32B61A8F9091002258B6 /* Resources */ = { 196 | isa = PBXResourcesBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | C5E027241AA6435E0032F2E9 /* red@2x.png in Resources */, 200 | C5E027231AA6435E0032F2E9 /* purple@2x.png in Resources */, 201 | C5AB32C31A8F9091002258B6 /* Images.xcassets in Resources */, 202 | C5E027291AA6435E0032F2E9 /* yellow@2x.png in Resources */, 203 | C5E027311AA64BD90032F2E9 /* question@2x.png in Resources */, 204 | C5E027281AA6435E0032F2E9 /* white@2x.png in Resources */, 205 | 7648BAC31AA5A50B003DA28F /* MainMenu.xib in Resources */, 206 | C5E027211AA6435E0032F2E9 /* green@2x.png in Resources */, 207 | C5E0272F1AA64BD90032F2E9 /* exclamation@2x.png in Resources */, 208 | C5E0271E1AA6435E0032F2E9 /* black@2x.png in Resources */, 209 | C5E027201AA6435E0032F2E9 /* cyan@2x.png in Resources */, 210 | C504CCFB25DEE28400DDEE71 /* filled@2x.png in Resources */, 211 | C5E0271F1AA6435E0032F2E9 /* blue@2x.png in Resources */, 212 | C5E027221AA6435E0032F2E9 /* orange@2x.png in Resources */, 213 | C03A3D3E1AA78DAD0030B668 /* AnyBar.sdef in Resources */, 214 | C504CCFC25DEE28400DDEE71 /* hollow@2x.png in Resources */, 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | /* End PBXResourcesBuildPhase section */ 219 | 220 | /* Begin PBXSourcesBuildPhase section */ 221 | C5AB32B41A8F9091002258B6 /* Sources */ = { 222 | isa = PBXSourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | C03A3D431AA7A6540030B668 /* AnyBarApp.m in Sources */, 226 | C5AB32E81A8F9B4E002258B6 /* GCDAsyncUdpSocket.m in Sources */, 227 | C5AB32E71A8F9B4E002258B6 /* GCDAsyncSocket.m in Sources */, 228 | C5AB32C11A8F9091002258B6 /* main.m in Sources */, 229 | C5AB32BF1A8F9091002258B6 /* AppDelegate.m in Sources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | /* End PBXSourcesBuildPhase section */ 234 | 235 | /* Begin PBXVariantGroup section */ 236 | C5AB32C41A8F9091002258B6 /* MainMenu.xib */ = { 237 | isa = PBXVariantGroup; 238 | children = ( 239 | C5AB32C51A8F9091002258B6 /* Base */, 240 | ); 241 | name = MainMenu.xib; 242 | sourceTree = ""; 243 | }; 244 | /* End PBXVariantGroup section */ 245 | 246 | /* Begin XCBuildConfiguration section */ 247 | C5AB32D31A8F9091002258B6 /* Debug */ = { 248 | isa = XCBuildConfiguration; 249 | buildSettings = { 250 | ALWAYS_SEARCH_USER_PATHS = NO; 251 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 252 | CLANG_CXX_LIBRARY = "libc++"; 253 | CLANG_ENABLE_MODULES = YES; 254 | CLANG_ENABLE_OBJC_ARC = YES; 255 | CLANG_WARN_BOOL_CONVERSION = YES; 256 | CLANG_WARN_CONSTANT_CONVERSION = YES; 257 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 258 | CLANG_WARN_EMPTY_BODY = YES; 259 | CLANG_WARN_ENUM_CONVERSION = YES; 260 | CLANG_WARN_INFINITE_RECURSION = YES; 261 | CLANG_WARN_INT_CONVERSION = YES; 262 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 263 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 264 | CLANG_WARN_UNREACHABLE_CODE = YES; 265 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 266 | CODE_SIGN_IDENTITY = "-"; 267 | COPY_PHASE_STRIP = NO; 268 | ENABLE_STRICT_OBJC_MSGSEND = YES; 269 | ENABLE_TESTABILITY = YES; 270 | GCC_C_LANGUAGE_STANDARD = gnu99; 271 | GCC_DYNAMIC_NO_PIC = NO; 272 | GCC_NO_COMMON_BLOCKS = YES; 273 | GCC_OPTIMIZATION_LEVEL = 0; 274 | GCC_PREPROCESSOR_DEFINITIONS = ( 275 | "DEBUG=1", 276 | "$(inherited)", 277 | ); 278 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 279 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 280 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 281 | GCC_WARN_UNDECLARED_SELECTOR = YES; 282 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 283 | GCC_WARN_UNUSED_FUNCTION = YES; 284 | GCC_WARN_UNUSED_VARIABLE = YES; 285 | MACOSX_DEPLOYMENT_TARGET = 10.9; 286 | MTL_ENABLE_DEBUG_INFO = YES; 287 | ONLY_ACTIVE_ARCH = YES; 288 | SDKROOT = macosx; 289 | }; 290 | name = Debug; 291 | }; 292 | C5AB32D41A8F9091002258B6 /* Release */ = { 293 | isa = XCBuildConfiguration; 294 | buildSettings = { 295 | ALWAYS_SEARCH_USER_PATHS = NO; 296 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 297 | CLANG_CXX_LIBRARY = "libc++"; 298 | CLANG_ENABLE_MODULES = YES; 299 | CLANG_ENABLE_OBJC_ARC = YES; 300 | CLANG_WARN_BOOL_CONVERSION = YES; 301 | CLANG_WARN_CONSTANT_CONVERSION = YES; 302 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 303 | CLANG_WARN_EMPTY_BODY = YES; 304 | CLANG_WARN_ENUM_CONVERSION = YES; 305 | CLANG_WARN_INFINITE_RECURSION = YES; 306 | CLANG_WARN_INT_CONVERSION = YES; 307 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 308 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 309 | CLANG_WARN_UNREACHABLE_CODE = YES; 310 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 311 | CODE_SIGN_IDENTITY = "-"; 312 | COPY_PHASE_STRIP = YES; 313 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 314 | ENABLE_NS_ASSERTIONS = NO; 315 | ENABLE_STRICT_OBJC_MSGSEND = YES; 316 | GCC_C_LANGUAGE_STANDARD = gnu99; 317 | GCC_NO_COMMON_BLOCKS = YES; 318 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 319 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 320 | GCC_WARN_UNDECLARED_SELECTOR = YES; 321 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 322 | GCC_WARN_UNUSED_FUNCTION = YES; 323 | GCC_WARN_UNUSED_VARIABLE = YES; 324 | MACOSX_DEPLOYMENT_TARGET = 10.9; 325 | MTL_ENABLE_DEBUG_INFO = NO; 326 | ONLY_ACTIVE_ARCH = NO; 327 | SDKROOT = macosx; 328 | }; 329 | name = Release; 330 | }; 331 | C5AB32D61A8F9091002258B6 /* Debug */ = { 332 | isa = XCBuildConfiguration; 333 | buildSettings = { 334 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 335 | COMBINE_HIDPI_IMAGES = YES; 336 | CURRENT_PROJECT_VERSION = 8; 337 | INFOPLIST_FILE = AnyBar/Info.plist; 338 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 339 | MACOSX_DEPLOYMENT_TARGET = 10.9; 340 | MARKETING_VERSION = 0.2.3; 341 | PRODUCT_BUNDLE_IDENTIFIER = "tonsky.$(PRODUCT_NAME:rfc1034identifier)"; 342 | PRODUCT_NAME = "$(TARGET_NAME)"; 343 | }; 344 | name = Debug; 345 | }; 346 | C5AB32D71A8F9091002258B6 /* Release */ = { 347 | isa = XCBuildConfiguration; 348 | buildSettings = { 349 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 350 | COMBINE_HIDPI_IMAGES = YES; 351 | CURRENT_PROJECT_VERSION = 8; 352 | INFOPLIST_FILE = AnyBar/Info.plist; 353 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 354 | MACOSX_DEPLOYMENT_TARGET = 10.9; 355 | MARKETING_VERSION = 0.2.3; 356 | PRODUCT_BUNDLE_IDENTIFIER = "tonsky.$(PRODUCT_NAME:rfc1034identifier)"; 357 | PRODUCT_NAME = "$(TARGET_NAME)"; 358 | }; 359 | name = Release; 360 | }; 361 | /* End XCBuildConfiguration section */ 362 | 363 | /* Begin XCConfigurationList section */ 364 | C5AB32B31A8F9091002258B6 /* Build configuration list for PBXProject "AnyBar" */ = { 365 | isa = XCConfigurationList; 366 | buildConfigurations = ( 367 | C5AB32D31A8F9091002258B6 /* Debug */, 368 | C5AB32D41A8F9091002258B6 /* Release */, 369 | ); 370 | defaultConfigurationIsVisible = 0; 371 | defaultConfigurationName = Release; 372 | }; 373 | C5AB32D51A8F9091002258B6 /* Build configuration list for PBXNativeTarget "AnyBar" */ = { 374 | isa = XCConfigurationList; 375 | buildConfigurations = ( 376 | C5AB32D61A8F9091002258B6 /* Debug */, 377 | C5AB32D71A8F9091002258B6 /* Release */, 378 | ); 379 | defaultConfigurationIsVisible = 0; 380 | defaultConfigurationName = Release; 381 | }; 382 | /* End XCConfigurationList section */ 383 | }; 384 | rootObject = C5AB32B01A8F9091002258B6 /* Project object */; 385 | } 386 | -------------------------------------------------------------------------------- /AnyBar.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AnyBar.xcodeproj/project.xcworkspace/xcshareddata/AnyBar.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | BA23A1B6-5DAC-472E-A098-51AA9470648F 9 | IDESourceControlProjectName 10 | AnyBar 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 724AB094F6D42B778A94AB549935CA62FD6441E1 14 | github.com:tonsky/AnyBar.git 15 | 16 | IDESourceControlProjectPath 17 | AnyBar.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 724AB094F6D42B778A94AB549935CA62FD6441E1 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | github.com:tonsky/AnyBar.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 724AB094F6D42B778A94AB549935CA62FD6441E1 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 724AB094F6D42B778A94AB549935CA62FD6441E1 36 | IDESourceControlWCCName 37 | AnyBar 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /AnyBar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AnyBar.xcodeproj/project.xcworkspace/xcuserdata/prokopov.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar.xcodeproj/project.xcworkspace/xcuserdata/prokopov.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AnyBar.xcodeproj/project.xcworkspace/xcuserdata/prokopov.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseAppPreferences 7 | CustomBuildLocationType 8 | RelativeToDerivedData 9 | DerivedDataLocationStyle 10 | Default 11 | IssueFilterStyle 12 | ShowActiveSchemeOnly 13 | LiveSourceIssuesEnabled 14 | 15 | SnapshotAutomaticallyBeforeSignificantChanges 16 | 17 | SnapshotLocationStyle 18 | Default 19 | 20 | 21 | -------------------------------------------------------------------------------- /AnyBar.xcodeproj/xcuserdata/prokopov.xcuserdatad/xcschemes/AnyBar.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /AnyBar.xcodeproj/xcuserdata/prokopov.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AnyBar.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | C5AB32B71A8F9091002258B6 16 | 17 | primary 18 | 19 | 20 | C5AB32CA1A8F9091002258B6 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /AnyBar/AnyBar.sdef: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /AnyBar/AnyBarApp.h: -------------------------------------------------------------------------------- 1 | // 2 | // AnyBarApp.h 3 | // AnyBar 4 | // 5 | // Created by Nikita Prokopov on 04/03/15. 6 | // Copyright (c) 2015 Nikita Prokopov. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | @interface AnyBarApp : NSApplication 13 | 14 | // 15 | // OSA Scripting endpoints 16 | // @see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_intro/SAppsIntro.html 17 | // @see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_about_apps/SAppsAboutApps.html 18 | // 19 | -(id) osaImage; 20 | -(void) setOsaImage:(id)imgName; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /AnyBar/AnyBarApp.m: -------------------------------------------------------------------------------- 1 | // 2 | // AnyBarApp.h 3 | // AnyBar 4 | // 5 | // Created by Nikita Prokopov on 04/03/15. 6 | // Copyright (c) 2015 Nikita Prokopov. All rights reserved. 7 | // 8 | 9 | #import "AnyBarApp.h" 10 | 11 | @implementation AnyBarApp 12 | 13 | -(id) osaImage { 14 | AppDelegate *delegate = (AppDelegate*)self.delegate; 15 | return [delegate osaImageBridge]; 16 | } 17 | 18 | -(void) setOsaImage:(id)imgName { 19 | AppDelegate *delegate = (AppDelegate*)self.delegate; 20 | [delegate setOsaImageBridge:imgName]; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /AnyBar/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // AnyBar 4 | // 5 | // Created by Nikita Prokopov on 14/02/15. 6 | // Copyright (c) 2015 Nikita Prokopov. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GCDAsyncUdpSocket.h" 11 | 12 | @interface AppDelegate : NSObject 13 | 14 | // 15 | // OSA Scripting bridge 16 | // @see AnyBarApp.h 17 | // 18 | -(id) osaImageBridge; 19 | -(void) setOsaImageBridge:(id)imgName; 20 | 21 | @end 22 | 23 | -------------------------------------------------------------------------------- /AnyBar/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // AnyBar 4 | // 5 | // Created by Nikita Prokopov on 14/02/15. 6 | // Copyright (c) 2015 Nikita Prokopov. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate() 12 | 13 | @property (weak, nonatomic) IBOutlet NSWindow *window; 14 | @property (strong, nonatomic) NSStatusItem *statusItem; 15 | @property (strong, nonatomic) GCDAsyncUdpSocket *udpSocket; 16 | @property (strong, nonatomic) NSString *imageName; 17 | @property (assign, nonatomic) int udpPort; 18 | @property (assign, nonatomic) NSString *appTitle; 19 | 20 | @end 21 | 22 | @implementation AppDelegate 23 | 24 | -(void)applicationDidFinishLaunching:(NSNotification *)aNotification { 25 | _udpPort = -1; 26 | _imageName = [self readStringFromEnvironmentVariable:@"ANYBAR_INIT" usingDefault:@"hollow"]; 27 | self.statusItem = [self initializeStatusBarItem]; 28 | [self setImage:_imageName]; 29 | 30 | @try { 31 | _udpPort = [self getUdpPort]; 32 | _udpSocket = [self initializeUdpSocket: _udpPort]; 33 | _appTitle = [self readStringFromEnvironmentVariable:@"ANYBAR_TITLE" usingDefault:nil]; 34 | _statusItem.toolTip = _appTitle == nil ? [NSString stringWithFormat:@"AnyBar @ %d", _udpPort] : _appTitle; 35 | } 36 | @catch(NSException *ex) { 37 | NSLog(@"Error: %@: %@", ex.name, ex.reason); 38 | _statusItem.image = [NSImage imageNamed:@"exclamation@2x.png"]; 39 | [_statusItem.image setTemplate:NO]; 40 | } 41 | @finally { 42 | NSString *portTitle = [NSString stringWithFormat:@"UDP port: %@", _udpPort >= 0 ? [NSNumber numberWithInt:_udpPort] : @"unavailable"]; 43 | NSMenu *menu = [[NSMenu alloc] init]; 44 | 45 | if (_appTitle != nil) 46 | [menu addItemWithTitle:_appTitle action:nil keyEquivalent:@""]; 47 | [menu addItemWithTitle:portTitle action:nil keyEquivalent:@""]; 48 | [menu addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@""]; 49 | 50 | _statusItem.menu = menu; 51 | } 52 | } 53 | 54 | -(void)applicationWillTerminate:(NSNotification *)aNotification { 55 | [self shutdownUdpSocket: _udpSocket]; 56 | _udpSocket = nil; 57 | 58 | [[NSStatusBar systemStatusBar] removeStatusItem:_statusItem]; 59 | _statusItem = nil; 60 | } 61 | 62 | -(int) getUdpPort { 63 | int port = [self readIntFromEnvironmentVariable:@"ANYBAR_PORT" usingDefault:@"1738"]; 64 | 65 | if (port < 0 || port > 65535) { 66 | @throw([NSException exceptionWithName:@"Argument Exception" 67 | reason:[NSString stringWithFormat:@"UDP Port range is invalid: %d", port] 68 | userInfo:@{@"argument": [NSNumber numberWithInt:port]}]); 69 | 70 | } 71 | 72 | return port; 73 | } 74 | 75 | -(GCDAsyncUdpSocket*)initializeUdpSocket:(int)port { 76 | NSError *error = nil; 77 | GCDAsyncUdpSocket *udpSocket = [[GCDAsyncUdpSocket alloc] 78 | initWithDelegate:self 79 | delegateQueue:dispatch_get_main_queue()]; 80 | 81 | [udpSocket bindToPort:port error:&error]; 82 | if (error) { 83 | @throw([NSException exceptionWithName:@"UDP Exception" 84 | reason:[NSString stringWithFormat:@"Binding to %d failed", port] 85 | userInfo:@{@"error": error}]); 86 | } 87 | 88 | [udpSocket beginReceiving:&error]; 89 | if (error) { 90 | @throw([NSException exceptionWithName:@"UDP Exception" 91 | reason:[NSString stringWithFormat:@"Receiving from %d failed", port] 92 | userInfo:@{@"error": error}]); 93 | } 94 | 95 | return udpSocket; 96 | } 97 | 98 | -(void)shutdownUdpSocket:(GCDAsyncUdpSocket*)sock { 99 | if (sock != nil) { 100 | [sock close]; 101 | } 102 | } 103 | 104 | -(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data 105 | fromAddress:(NSData *)address withFilterContext:(id)filterContext { 106 | [self processUdpSocketMsg:sock withData:data fromAddress:address]; 107 | } 108 | 109 | -(NSImage*)tryImage:(NSString *)path { 110 | NSFileManager *fileManager = [NSFileManager defaultManager]; 111 | if ([fileManager fileExistsAtPath:path]) 112 | return [[NSImage alloc] initWithContentsOfFile:path]; 113 | else 114 | return nil; 115 | } 116 | 117 | -(NSString*)bundledImagePath:(NSString *)name { 118 | return [[NSBundle mainBundle] pathForResource:name ofType:@"png"]; 119 | } 120 | 121 | -(NSString*)homedirImagePath:(NSString *)name { 122 | return [NSString stringWithFormat:@"%@/%@/%@.png", NSHomeDirectory(), @".AnyBar", name]; 123 | } 124 | 125 | -(void)setImage:(NSString*) name { 126 | NSImage *image = nil; 127 | image = [self tryImage:[self homedirImagePath:[name stringByAppendingString:@"@2x"]]]; 128 | if (!image) 129 | image = [self tryImage:[self homedirImagePath:name]]; 130 | if (!image) 131 | image = [self tryImage:[self bundledImagePath:[name stringByAppendingString:@"@2x"]]]; 132 | if (!image) 133 | image = [self tryImage:[self bundledImagePath:name]]; 134 | if (!image) { 135 | NSLog(@"Cannot find image '%@'", name); 136 | image = [self tryImage:[self bundledImagePath:@"question@2x"]]; 137 | _statusItem.image = image; 138 | [_statusItem.image setTemplate:NO]; 139 | } else { 140 | _statusItem.image = image; 141 | if ([name isEqualToString:@"filled"] || [name isEqualToString:@"hollow"]) 142 | [_statusItem.image setTemplate:YES]; 143 | else 144 | [_statusItem.image setTemplate:NO]; 145 | _imageName = name; 146 | } 147 | } 148 | 149 | -(void)processUdpSocketMsg:(GCDAsyncUdpSocket *)sock withData:(NSData *)data 150 | fromAddress:(NSData *)address { 151 | NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 152 | 153 | if ([msg isEqualToString:@"quit"]) 154 | [[NSApplication sharedApplication] terminate:nil]; 155 | else 156 | [self setImage:msg]; 157 | } 158 | 159 | -(NSStatusItem*) initializeStatusBarItem { 160 | NSStatusItem *statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; 161 | // statusItem.image = [NSImage imageNamed:@"white@2x.png"]; 162 | statusItem.highlightMode = YES; 163 | return statusItem; 164 | } 165 | 166 | -(int) readIntFromEnvironmentVariable:(NSString*) envVariable usingDefault:(NSString*) defStr { 167 | int intVal = -1; 168 | 169 | NSString *envStr = [[[NSProcessInfo processInfo] 170 | environment] objectForKey:envVariable]; 171 | if (!envStr) { 172 | envStr = defStr; 173 | } 174 | 175 | NSNumberFormatter *nFormatter = [[NSNumberFormatter alloc] init]; 176 | nFormatter.numberStyle = NSNumberFormatterDecimalStyle; 177 | NSNumber *number = [nFormatter numberFromString:envStr]; 178 | 179 | if (!number) { 180 | @throw([NSException exceptionWithName:@"Argument Exception" 181 | reason:[NSString stringWithFormat:@"Parsing integer from %@ failed", envStr] 182 | userInfo:@{@"argument": envStr}]); 183 | 184 | } 185 | 186 | intVal = [number intValue]; 187 | 188 | return intVal; 189 | } 190 | 191 | -(NSString*) readStringFromEnvironmentVariable:(NSString*) envVariable usingDefault:(NSString*) defStr { 192 | NSString *envStr = [[[NSProcessInfo processInfo] 193 | environment] objectForKey:envVariable]; 194 | 195 | if (!envStr) { 196 | envStr = defStr; 197 | } 198 | 199 | return envStr; 200 | } 201 | 202 | -(id) osaImageBridge { 203 | NSLog(@"OSA Event: %@ - %@", NSStringFromSelector(_cmd), _imageName); 204 | 205 | return _imageName; 206 | } 207 | 208 | 209 | -(void) setOsaImageBridge:(id)imgName { 210 | NSLog(@"OSA Event: %@ - %@", NSStringFromSelector(_cmd), imgName); 211 | 212 | _imageName = (NSString *)imgName; 213 | 214 | [self setImage:_imageName]; 215 | } 216 | 217 | @end 218 | 219 | -------------------------------------------------------------------------------- /AnyBar/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /AnyBar/GCDAsyncSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // GCDAsyncSocket.h 3 | // 4 | // This class is in the public domain. 5 | // Originally created by Robbie Hanson in Q3 2010. 6 | // Updated and maintained by Deusty LLC and the Apple development community. 7 | // 8 | // https://github.com/robbiehanson/CocoaAsyncSocket 9 | // 10 | 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | #include // AF_INET, AF_INET6 18 | 19 | @class GCDAsyncReadPacket; 20 | @class GCDAsyncWritePacket; 21 | @class GCDAsyncSocketPreBuffer; 22 | 23 | extern NSString *const GCDAsyncSocketException; 24 | extern NSString *const GCDAsyncSocketErrorDomain; 25 | 26 | extern NSString *const GCDAsyncSocketQueueName; 27 | extern NSString *const GCDAsyncSocketThreadName; 28 | 29 | extern NSString *const GCDAsyncSocketManuallyEvaluateTrust; 30 | #if TARGET_OS_IPHONE 31 | extern NSString *const GCDAsyncSocketUseCFStreamForTLS; 32 | #endif 33 | #define GCDAsyncSocketSSLPeerName (NSString *)kCFStreamSSLPeerName 34 | #define GCDAsyncSocketSSLCertificates (NSString *)kCFStreamSSLCertificates 35 | #define GCDAsyncSocketSSLIsServer (NSString *)kCFStreamSSLIsServer 36 | extern NSString *const GCDAsyncSocketSSLPeerID; 37 | extern NSString *const GCDAsyncSocketSSLProtocolVersionMin; 38 | extern NSString *const GCDAsyncSocketSSLProtocolVersionMax; 39 | extern NSString *const GCDAsyncSocketSSLSessionOptionFalseStart; 40 | extern NSString *const GCDAsyncSocketSSLSessionOptionSendOneByteRecord; 41 | extern NSString *const GCDAsyncSocketSSLCipherSuites; 42 | #if !TARGET_OS_IPHONE 43 | extern NSString *const GCDAsyncSocketSSLDiffieHellmanParameters; 44 | #endif 45 | 46 | #define GCDAsyncSocketLoggingContext 65535 47 | 48 | 49 | enum GCDAsyncSocketError 50 | { 51 | GCDAsyncSocketNoError = 0, // Never used 52 | GCDAsyncSocketBadConfigError, // Invalid configuration 53 | GCDAsyncSocketBadParamError, // Invalid parameter was passed 54 | GCDAsyncSocketConnectTimeoutError, // A connect operation timed out 55 | GCDAsyncSocketReadTimeoutError, // A read operation timed out 56 | GCDAsyncSocketWriteTimeoutError, // A write operation timed out 57 | GCDAsyncSocketReadMaxedOutError, // Reached set maxLength without completing 58 | GCDAsyncSocketClosedError, // The remote peer closed the connection 59 | GCDAsyncSocketOtherError, // Description provided in userInfo 60 | }; 61 | typedef enum GCDAsyncSocketError GCDAsyncSocketError; 62 | 63 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 64 | #pragma mark - 65 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 66 | 67 | @interface GCDAsyncSocket : NSObject 68 | 69 | /** 70 | * GCDAsyncSocket uses the standard delegate paradigm, 71 | * but executes all delegate callbacks on a given delegate dispatch queue. 72 | * This allows for maximum concurrency, while at the same time providing easy thread safety. 73 | * 74 | * You MUST set a delegate AND delegate dispatch queue before attempting to 75 | * use the socket, or you will get an error. 76 | * 77 | * The socket queue is optional. 78 | * If you pass NULL, GCDAsyncSocket will automatically create it's own socket queue. 79 | * If you choose to provide a socket queue, the socket queue must not be a concurrent queue. 80 | * If you choose to provide a socket queue, and the socket queue has a configured target queue, 81 | * then please see the discussion for the method markSocketQueueTargetQueue. 82 | * 83 | * The delegate queue and socket queue can optionally be the same. 84 | **/ 85 | - (id)init; 86 | - (id)initWithSocketQueue:(dispatch_queue_t)sq; 87 | - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq; 88 | - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq; 89 | 90 | #pragma mark Configuration 91 | 92 | @property (atomic, weak, readwrite) id delegate; 93 | #if OS_OBJECT_USE_OBJC 94 | @property (atomic, strong, readwrite) dispatch_queue_t delegateQueue; 95 | #else 96 | @property (atomic, assign, readwrite) dispatch_queue_t delegateQueue; 97 | #endif 98 | 99 | - (void)getDelegate:(id *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr; 100 | - (void)setDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue; 101 | 102 | /** 103 | * If you are setting the delegate to nil within the delegate's dealloc method, 104 | * you may need to use the synchronous versions below. 105 | **/ 106 | - (void)synchronouslySetDelegate:(id)delegate; 107 | - (void)synchronouslySetDelegateQueue:(dispatch_queue_t)delegateQueue; 108 | - (void)synchronouslySetDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue; 109 | 110 | /** 111 | * By default, both IPv4 and IPv6 are enabled. 112 | * 113 | * For accepting incoming connections, this means GCDAsyncSocket automatically supports both protocols, 114 | * and can simulataneously accept incoming connections on either protocol. 115 | * 116 | * For outgoing connections, this means GCDAsyncSocket can connect to remote hosts running either protocol. 117 | * If a DNS lookup returns only IPv4 results, GCDAsyncSocket will automatically use IPv4. 118 | * If a DNS lookup returns only IPv6 results, GCDAsyncSocket will automatically use IPv6. 119 | * If a DNS lookup returns both IPv4 and IPv6 results, the preferred protocol will be chosen. 120 | * By default, the preferred protocol is IPv4, but may be configured as desired. 121 | **/ 122 | 123 | @property (atomic, assign, readwrite, getter=isIPv4Enabled) BOOL IPv4Enabled; 124 | @property (atomic, assign, readwrite, getter=isIPv6Enabled) BOOL IPv6Enabled; 125 | 126 | @property (atomic, assign, readwrite, getter=isIPv4PreferredOverIPv6) BOOL IPv4PreferredOverIPv6; 127 | 128 | /** 129 | * User data allows you to associate arbitrary information with the socket. 130 | * This data is not used internally by socket in any way. 131 | **/ 132 | @property (atomic, strong, readwrite) id userData; 133 | 134 | #pragma mark Accepting 135 | 136 | /** 137 | * Tells the socket to begin listening and accepting connections on the given port. 138 | * When a connection is accepted, a new instance of GCDAsyncSocket will be spawned to handle it, 139 | * and the socket:didAcceptNewSocket: delegate method will be invoked. 140 | * 141 | * The socket will listen on all available interfaces (e.g. wifi, ethernet, etc) 142 | **/ 143 | - (BOOL)acceptOnPort:(uint16_t)port error:(NSError **)errPtr; 144 | 145 | /** 146 | * This method is the same as acceptOnPort:error: with the 147 | * additional option of specifying which interface to listen on. 148 | * 149 | * For example, you could specify that the socket should only accept connections over ethernet, 150 | * and not other interfaces such as wifi. 151 | * 152 | * The interface may be specified by name (e.g. "en1" or "lo0") or by IP address (e.g. "192.168.4.34"). 153 | * You may also use the special strings "localhost" or "loopback" to specify that 154 | * the socket only accept connections from the local machine. 155 | * 156 | * You can see the list of interfaces via the command line utility "ifconfig", 157 | * or programmatically via the getifaddrs() function. 158 | * 159 | * To accept connections on any interface pass nil, or simply use the acceptOnPort:error: method. 160 | **/ 161 | - (BOOL)acceptOnInterface:(NSString *)interface port:(uint16_t)port error:(NSError **)errPtr; 162 | 163 | #pragma mark Connecting 164 | 165 | /** 166 | * Connects to the given host and port. 167 | * 168 | * This method invokes connectToHost:onPort:viaInterface:withTimeout:error: 169 | * and uses the default interface, and no timeout. 170 | **/ 171 | - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr; 172 | 173 | /** 174 | * Connects to the given host and port with an optional timeout. 175 | * 176 | * This method invokes connectToHost:onPort:viaInterface:withTimeout:error: and uses the default interface. 177 | **/ 178 | - (BOOL)connectToHost:(NSString *)host 179 | onPort:(uint16_t)port 180 | withTimeout:(NSTimeInterval)timeout 181 | error:(NSError **)errPtr; 182 | 183 | /** 184 | * Connects to the given host & port, via the optional interface, with an optional timeout. 185 | * 186 | * The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). 187 | * The host may also be the special strings "localhost" or "loopback" to specify connecting 188 | * to a service on the local machine. 189 | * 190 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). 191 | * The interface may also be used to specify the local port (see below). 192 | * 193 | * To not time out use a negative time interval. 194 | * 195 | * This method will return NO if an error is detected, and set the error pointer (if one was given). 196 | * Possible errors would be a nil host, invalid interface, or socket is already connected. 197 | * 198 | * If no errors are detected, this method will start a background connect operation and immediately return YES. 199 | * The delegate callbacks are used to notify you when the socket connects, or if the host was unreachable. 200 | * 201 | * Since this class supports queued reads and writes, you can immediately start reading and/or writing. 202 | * All read/write operations will be queued, and upon socket connection, 203 | * the operations will be dequeued and processed in order. 204 | * 205 | * The interface may optionally contain a port number at the end of the string, separated by a colon. 206 | * This allows you to specify the local port that should be used for the outgoing connection. (read paragraph to end) 207 | * To specify both interface and local port: "en1:8082" or "192.168.4.35:2424". 208 | * To specify only local port: ":8082". 209 | * Please note this is an advanced feature, and is somewhat hidden on purpose. 210 | * You should understand that 99.999% of the time you should NOT specify the local port for an outgoing connection. 211 | * If you think you need to, there is a very good chance you have a fundamental misunderstanding somewhere. 212 | * Local ports do NOT need to match remote ports. In fact, they almost never do. 213 | * This feature is here for networking professionals using very advanced techniques. 214 | **/ 215 | - (BOOL)connectToHost:(NSString *)host 216 | onPort:(uint16_t)port 217 | viaInterface:(NSString *)interface 218 | withTimeout:(NSTimeInterval)timeout 219 | error:(NSError **)errPtr; 220 | 221 | /** 222 | * Connects to the given address, specified as a sockaddr structure wrapped in a NSData object. 223 | * For example, a NSData object returned from NSNetService's addresses method. 224 | * 225 | * If you have an existing struct sockaddr you can convert it to a NSData object like so: 226 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; 227 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; 228 | * 229 | * This method invokes connectToAdd 230 | **/ 231 | - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; 232 | 233 | /** 234 | * This method is the same as connectToAddress:error: with an additional timeout option. 235 | * To not time out use a negative time interval, or simply use the connectToAddress:error: method. 236 | **/ 237 | - (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr; 238 | 239 | /** 240 | * Connects to the given address, using the specified interface and timeout. 241 | * 242 | * The address is specified as a sockaddr structure wrapped in a NSData object. 243 | * For example, a NSData object returned from NSNetService's addresses method. 244 | * 245 | * If you have an existing struct sockaddr you can convert it to a NSData object like so: 246 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; 247 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; 248 | * 249 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). 250 | * The interface may also be used to specify the local port (see below). 251 | * 252 | * The timeout is optional. To not time out use a negative time interval. 253 | * 254 | * This method will return NO if an error is detected, and set the error pointer (if one was given). 255 | * Possible errors would be a nil host, invalid interface, or socket is already connected. 256 | * 257 | * If no errors are detected, this method will start a background connect operation and immediately return YES. 258 | * The delegate callbacks are used to notify you when the socket connects, or if the host was unreachable. 259 | * 260 | * Since this class supports queued reads and writes, you can immediately start reading and/or writing. 261 | * All read/write operations will be queued, and upon socket connection, 262 | * the operations will be dequeued and processed in order. 263 | * 264 | * The interface may optionally contain a port number at the end of the string, separated by a colon. 265 | * This allows you to specify the local port that should be used for the outgoing connection. (read paragraph to end) 266 | * To specify both interface and local port: "en1:8082" or "192.168.4.35:2424". 267 | * To specify only local port: ":8082". 268 | * Please note this is an advanced feature, and is somewhat hidden on purpose. 269 | * You should understand that 99.999% of the time you should NOT specify the local port for an outgoing connection. 270 | * If you think you need to, there is a very good chance you have a fundamental misunderstanding somewhere. 271 | * Local ports do NOT need to match remote ports. In fact, they almost never do. 272 | * This feature is here for networking professionals using very advanced techniques. 273 | **/ 274 | - (BOOL)connectToAddress:(NSData *)remoteAddr 275 | viaInterface:(NSString *)interface 276 | withTimeout:(NSTimeInterval)timeout 277 | error:(NSError **)errPtr; 278 | 279 | #pragma mark Disconnecting 280 | 281 | /** 282 | * Disconnects immediately (synchronously). Any pending reads or writes are dropped. 283 | * 284 | * If the socket is not already disconnected, an invocation to the socketDidDisconnect:withError: delegate method 285 | * will be queued onto the delegateQueue asynchronously (behind any previously queued delegate methods). 286 | * In other words, the disconnected delegate method will be invoked sometime shortly after this method returns. 287 | * 288 | * Please note the recommended way of releasing a GCDAsyncSocket instance (e.g. in a dealloc method) 289 | * [asyncSocket setDelegate:nil]; 290 | * [asyncSocket disconnect]; 291 | * [asyncSocket release]; 292 | * 293 | * If you plan on disconnecting the socket, and then immediately asking it to connect again, 294 | * you'll likely want to do so like this: 295 | * [asyncSocket setDelegate:nil]; 296 | * [asyncSocket disconnect]; 297 | * [asyncSocket setDelegate:self]; 298 | * [asyncSocket connect...]; 299 | **/ 300 | - (void)disconnect; 301 | 302 | /** 303 | * Disconnects after all pending reads have completed. 304 | * After calling this, the read and write methods will do nothing. 305 | * The socket will disconnect even if there are still pending writes. 306 | **/ 307 | - (void)disconnectAfterReading; 308 | 309 | /** 310 | * Disconnects after all pending writes have completed. 311 | * After calling this, the read and write methods will do nothing. 312 | * The socket will disconnect even if there are still pending reads. 313 | **/ 314 | - (void)disconnectAfterWriting; 315 | 316 | /** 317 | * Disconnects after all pending reads and writes have completed. 318 | * After calling this, the read and write methods will do nothing. 319 | **/ 320 | - (void)disconnectAfterReadingAndWriting; 321 | 322 | #pragma mark Diagnostics 323 | 324 | /** 325 | * Returns whether the socket is disconnected or connected. 326 | * 327 | * A disconnected socket may be recycled. 328 | * That is, it can used again for connecting or listening. 329 | * 330 | * If a socket is in the process of connecting, it may be neither disconnected nor connected. 331 | **/ 332 | @property (atomic, readonly) BOOL isDisconnected; 333 | @property (atomic, readonly) BOOL isConnected; 334 | 335 | /** 336 | * Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected. 337 | * The host will be an IP address. 338 | **/ 339 | @property (atomic, readonly) NSString *connectedHost; 340 | @property (atomic, readonly) uint16_t connectedPort; 341 | 342 | @property (atomic, readonly) NSString *localHost; 343 | @property (atomic, readonly) uint16_t localPort; 344 | 345 | /** 346 | * Returns the local or remote address to which this socket is connected, 347 | * specified as a sockaddr structure wrapped in a NSData object. 348 | * 349 | * @seealso connectedHost 350 | * @seealso connectedPort 351 | * @seealso localHost 352 | * @seealso localPort 353 | **/ 354 | @property (atomic, readonly) NSData *connectedAddress; 355 | @property (atomic, readonly) NSData *localAddress; 356 | 357 | /** 358 | * Returns whether the socket is IPv4 or IPv6. 359 | * An accepting socket may be both. 360 | **/ 361 | @property (atomic, readonly) BOOL isIPv4; 362 | @property (atomic, readonly) BOOL isIPv6; 363 | 364 | /** 365 | * Returns whether or not the socket has been secured via SSL/TLS. 366 | * 367 | * See also the startTLS method. 368 | **/ 369 | @property (atomic, readonly) BOOL isSecure; 370 | 371 | #pragma mark Reading 372 | 373 | // The readData and writeData methods won't block (they are asynchronous). 374 | // 375 | // When a read is complete the socket:didReadData:withTag: delegate method is dispatched on the delegateQueue. 376 | // When a write is complete the socket:didWriteDataWithTag: delegate method is dispatched on the delegateQueue. 377 | // 378 | // You may optionally set a timeout for any read/write operation. (To not timeout, use a negative time interval.) 379 | // If a read/write opertion times out, the corresponding "socket:shouldTimeout..." delegate method 380 | // is called to optionally allow you to extend the timeout. 381 | // Upon a timeout, the "socket:didDisconnectWithError:" method is called 382 | // 383 | // The tag is for your convenience. 384 | // You can use it as an array index, step number, state id, pointer, etc. 385 | 386 | /** 387 | * Reads the first available bytes that become available on the socket. 388 | * 389 | * If the timeout value is negative, the read operation will not use a timeout. 390 | **/ 391 | - (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag; 392 | 393 | /** 394 | * Reads the first available bytes that become available on the socket. 395 | * The bytes will be appended to the given byte buffer starting at the given offset. 396 | * The given buffer will automatically be increased in size if needed. 397 | * 398 | * If the timeout value is negative, the read operation will not use a timeout. 399 | * If the buffer if nil, the socket will create a buffer for you. 400 | * 401 | * If the bufferOffset is greater than the length of the given buffer, 402 | * the method will do nothing, and the delegate will not be called. 403 | * 404 | * If you pass a buffer, you must not alter it in any way while the socket is using it. 405 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 406 | * That is, it will reference the bytes that were appended to the given buffer via 407 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. 408 | **/ 409 | - (void)readDataWithTimeout:(NSTimeInterval)timeout 410 | buffer:(NSMutableData *)buffer 411 | bufferOffset:(NSUInteger)offset 412 | tag:(long)tag; 413 | 414 | /** 415 | * Reads the first available bytes that become available on the socket. 416 | * The bytes will be appended to the given byte buffer starting at the given offset. 417 | * The given buffer will automatically be increased in size if needed. 418 | * A maximum of length bytes will be read. 419 | * 420 | * If the timeout value is negative, the read operation will not use a timeout. 421 | * If the buffer if nil, a buffer will automatically be created for you. 422 | * If maxLength is zero, no length restriction is enforced. 423 | * 424 | * If the bufferOffset is greater than the length of the given buffer, 425 | * the method will do nothing, and the delegate will not be called. 426 | * 427 | * If you pass a buffer, you must not alter it in any way while the socket is using it. 428 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 429 | * That is, it will reference the bytes that were appended to the given buffer via 430 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. 431 | **/ 432 | - (void)readDataWithTimeout:(NSTimeInterval)timeout 433 | buffer:(NSMutableData *)buffer 434 | bufferOffset:(NSUInteger)offset 435 | maxLength:(NSUInteger)length 436 | tag:(long)tag; 437 | 438 | /** 439 | * Reads the given number of bytes. 440 | * 441 | * If the timeout value is negative, the read operation will not use a timeout. 442 | * 443 | * If the length is 0, this method does nothing and the delegate is not called. 444 | **/ 445 | - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag; 446 | 447 | /** 448 | * Reads the given number of bytes. 449 | * The bytes will be appended to the given byte buffer starting at the given offset. 450 | * The given buffer will automatically be increased in size if needed. 451 | * 452 | * If the timeout value is negative, the read operation will not use a timeout. 453 | * If the buffer if nil, a buffer will automatically be created for you. 454 | * 455 | * If the length is 0, this method does nothing and the delegate is not called. 456 | * If the bufferOffset is greater than the length of the given buffer, 457 | * the method will do nothing, and the delegate will not be called. 458 | * 459 | * If you pass a buffer, you must not alter it in any way while AsyncSocket is using it. 460 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 461 | * That is, it will reference the bytes that were appended to the given buffer via 462 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. 463 | **/ 464 | - (void)readDataToLength:(NSUInteger)length 465 | withTimeout:(NSTimeInterval)timeout 466 | buffer:(NSMutableData *)buffer 467 | bufferOffset:(NSUInteger)offset 468 | tag:(long)tag; 469 | 470 | /** 471 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. 472 | * 473 | * If the timeout value is negative, the read operation will not use a timeout. 474 | * 475 | * If you pass nil or zero-length data as the "data" parameter, 476 | * the method will do nothing (except maybe print a warning), and the delegate will not be called. 477 | * 478 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. 479 | * If you're developing your own custom protocol, be sure your separator can not occur naturally as 480 | * part of the data between separators. 481 | * For example, imagine you want to send several small documents over a socket. 482 | * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents. 483 | * In this particular example, it would be better to use a protocol similar to HTTP with 484 | * a header that includes the length of the document. 485 | * Also be careful that your separator cannot occur naturally as part of the encoding for a character. 486 | * 487 | * The given data (separator) parameter should be immutable. 488 | * For performance reasons, the socket will retain it, not copy it. 489 | * So if it is immutable, don't modify it while the socket is using it. 490 | **/ 491 | - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; 492 | 493 | /** 494 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. 495 | * The bytes will be appended to the given byte buffer starting at the given offset. 496 | * The given buffer will automatically be increased in size if needed. 497 | * 498 | * If the timeout value is negative, the read operation will not use a timeout. 499 | * If the buffer if nil, a buffer will automatically be created for you. 500 | * 501 | * If the bufferOffset is greater than the length of the given buffer, 502 | * the method will do nothing (except maybe print a warning), and the delegate will not be called. 503 | * 504 | * If you pass a buffer, you must not alter it in any way while the socket is using it. 505 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 506 | * That is, it will reference the bytes that were appended to the given buffer via 507 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. 508 | * 509 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. 510 | * If you're developing your own custom protocol, be sure your separator can not occur naturally as 511 | * part of the data between separators. 512 | * For example, imagine you want to send several small documents over a socket. 513 | * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents. 514 | * In this particular example, it would be better to use a protocol similar to HTTP with 515 | * a header that includes the length of the document. 516 | * Also be careful that your separator cannot occur naturally as part of the encoding for a character. 517 | * 518 | * The given data (separator) parameter should be immutable. 519 | * For performance reasons, the socket will retain it, not copy it. 520 | * So if it is immutable, don't modify it while the socket is using it. 521 | **/ 522 | - (void)readDataToData:(NSData *)data 523 | withTimeout:(NSTimeInterval)timeout 524 | buffer:(NSMutableData *)buffer 525 | bufferOffset:(NSUInteger)offset 526 | tag:(long)tag; 527 | 528 | /** 529 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. 530 | * 531 | * If the timeout value is negative, the read operation will not use a timeout. 532 | * 533 | * If maxLength is zero, no length restriction is enforced. 534 | * Otherwise if maxLength bytes are read without completing the read, 535 | * it is treated similarly to a timeout - the socket is closed with a GCDAsyncSocketReadMaxedOutError. 536 | * The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end. 537 | * 538 | * If you pass nil or zero-length data as the "data" parameter, 539 | * the method will do nothing (except maybe print a warning), and the delegate will not be called. 540 | * If you pass a maxLength parameter that is less than the length of the data parameter, 541 | * the method will do nothing (except maybe print a warning), and the delegate will not be called. 542 | * 543 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. 544 | * If you're developing your own custom protocol, be sure your separator can not occur naturally as 545 | * part of the data between separators. 546 | * For example, imagine you want to send several small documents over a socket. 547 | * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents. 548 | * In this particular example, it would be better to use a protocol similar to HTTP with 549 | * a header that includes the length of the document. 550 | * Also be careful that your separator cannot occur naturally as part of the encoding for a character. 551 | * 552 | * The given data (separator) parameter should be immutable. 553 | * For performance reasons, the socket will retain it, not copy it. 554 | * So if it is immutable, don't modify it while the socket is using it. 555 | **/ 556 | - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(NSUInteger)length tag:(long)tag; 557 | 558 | /** 559 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. 560 | * The bytes will be appended to the given byte buffer starting at the given offset. 561 | * The given buffer will automatically be increased in size if needed. 562 | * 563 | * If the timeout value is negative, the read operation will not use a timeout. 564 | * If the buffer if nil, a buffer will automatically be created for you. 565 | * 566 | * If maxLength is zero, no length restriction is enforced. 567 | * Otherwise if maxLength bytes are read without completing the read, 568 | * it is treated similarly to a timeout - the socket is closed with a GCDAsyncSocketReadMaxedOutError. 569 | * The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end. 570 | * 571 | * If you pass a maxLength parameter that is less than the length of the data (separator) parameter, 572 | * the method will do nothing (except maybe print a warning), and the delegate will not be called. 573 | * If the bufferOffset is greater than the length of the given buffer, 574 | * the method will do nothing (except maybe print a warning), and the delegate will not be called. 575 | * 576 | * If you pass a buffer, you must not alter it in any way while the socket is using it. 577 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 578 | * That is, it will reference the bytes that were appended to the given buffer via 579 | * the method [NSData dataWithBytesNoCopy:length:freeWhenDone:NO]. 580 | * 581 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. 582 | * If you're developing your own custom protocol, be sure your separator can not occur naturally as 583 | * part of the data between separators. 584 | * For example, imagine you want to send several small documents over a socket. 585 | * Using CRLF as a separator is likely unwise, as a CRLF could easily exist within the documents. 586 | * In this particular example, it would be better to use a protocol similar to HTTP with 587 | * a header that includes the length of the document. 588 | * Also be careful that your separator cannot occur naturally as part of the encoding for a character. 589 | * 590 | * The given data (separator) parameter should be immutable. 591 | * For performance reasons, the socket will retain it, not copy it. 592 | * So if it is immutable, don't modify it while the socket is using it. 593 | **/ 594 | - (void)readDataToData:(NSData *)data 595 | withTimeout:(NSTimeInterval)timeout 596 | buffer:(NSMutableData *)buffer 597 | bufferOffset:(NSUInteger)offset 598 | maxLength:(NSUInteger)length 599 | tag:(long)tag; 600 | 601 | /** 602 | * Returns progress of the current read, from 0.0 to 1.0, or NaN if no current read (use isnan() to check). 603 | * The parameters "tag", "done" and "total" will be filled in if they aren't NULL. 604 | **/ 605 | - (float)progressOfReadReturningTag:(long *)tagPtr bytesDone:(NSUInteger *)donePtr total:(NSUInteger *)totalPtr; 606 | 607 | #pragma mark Writing 608 | 609 | /** 610 | * Writes data to the socket, and calls the delegate when finished. 611 | * 612 | * If you pass in nil or zero-length data, this method does nothing and the delegate will not be called. 613 | * If the timeout value is negative, the write operation will not use a timeout. 614 | * 615 | * Thread-Safety Note: 616 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while 617 | * the socket is writing it. In other words, it's not safe to alter the data until after the delegate method 618 | * socket:didWriteDataWithTag: is invoked signifying that this particular write operation has completed. 619 | * This is due to the fact that GCDAsyncSocket does NOT copy the data. It simply retains it. 620 | * This is for performance reasons. Often times, if NSMutableData is passed, it is because 621 | * a request/response was built up in memory. Copying this data adds an unwanted/unneeded overhead. 622 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket 623 | * completes writing the bytes (which is NOT immediately after this method returns, but rather at a later time 624 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. 625 | **/ 626 | - (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; 627 | 628 | /** 629 | * Returns progress of the current write, from 0.0 to 1.0, or NaN if no current write (use isnan() to check). 630 | * The parameters "tag", "done" and "total" will be filled in if they aren't NULL. 631 | **/ 632 | - (float)progressOfWriteReturningTag:(long *)tagPtr bytesDone:(NSUInteger *)donePtr total:(NSUInteger *)totalPtr; 633 | 634 | #pragma mark Security 635 | 636 | /** 637 | * Secures the connection using SSL/TLS. 638 | * 639 | * This method may be called at any time, and the TLS handshake will occur after all pending reads and writes 640 | * are finished. This allows one the option of sending a protocol dependent StartTLS message, and queuing 641 | * the upgrade to TLS at the same time, without having to wait for the write to finish. 642 | * Any reads or writes scheduled after this method is called will occur over the secured connection. 643 | * 644 | * ==== The available TOP-LEVEL KEYS are: 645 | * 646 | * - GCDAsyncSocketManuallyEvaluateTrust 647 | * The value must be of type NSNumber, encapsulating a BOOL value. 648 | * If you set this to YES, then the underlying SecureTransport system will not evaluate the SecTrustRef of the peer. 649 | * Instead it will pause at the moment evaulation would typically occur, 650 | * and allow us to handle the security evaluation however we see fit. 651 | * So GCDAsyncSocket will invoke the delegate method socket:shouldTrustPeer: passing the SecTrustRef. 652 | * 653 | * Note that if you set this option, then all other configuration keys are ignored. 654 | * Evaluation will be completely up to you during the socket:didReceiveTrust:completionHandler: delegate method. 655 | * 656 | * For more information on trust evaluation see: 657 | * Apple's Technical Note TN2232 - HTTPS Server Trust Evaluation 658 | * https://developer.apple.com/library/ios/technotes/tn2232/_index.html 659 | * 660 | * If unspecified, the default value is NO. 661 | * 662 | * - GCDAsyncSocketUseCFStreamForTLS (iOS only) 663 | * The value must be of type NSNumber, encapsulating a BOOL value. 664 | * By default GCDAsyncSocket will use the SecureTransport layer to perform encryption. 665 | * This gives us more control over the security protocol (many more configuration options), 666 | * plus it allows us to optimize things like sys calls and buffer allocation. 667 | * 668 | * However, if you absolutely must, you can instruct GCDAsyncSocket to use the old-fashioned encryption 669 | * technique by going through the CFStream instead. So instead of using SecureTransport, GCDAsyncSocket 670 | * will instead setup a CFRead/CFWriteStream. And then set the kCFStreamPropertySSLSettings property 671 | * (via CFReadStreamSetProperty / CFWriteStreamSetProperty) and will pass the given options to this method. 672 | * 673 | * Thus all the other keys in the given dictionary will be ignored by GCDAsyncSocket, 674 | * and will passed directly CFReadStreamSetProperty / CFWriteStreamSetProperty. 675 | * For more infomation on these keys, please see the documentation for kCFStreamPropertySSLSettings. 676 | * 677 | * If unspecified, the default value is NO. 678 | * 679 | * ==== The available CONFIGURATION KEYS are: 680 | * 681 | * - kCFStreamSSLPeerName 682 | * The value must be of type NSString. 683 | * It should match the name in the X.509 certificate given by the remote party. 684 | * See Apple's documentation for SSLSetPeerDomainName. 685 | * 686 | * - kCFStreamSSLCertificates 687 | * The value must be of type NSArray. 688 | * See Apple's documentation for SSLSetCertificate. 689 | * 690 | * - kCFStreamSSLIsServer 691 | * The value must be of type NSNumber, encapsulationg a BOOL value. 692 | * See Apple's documentation for SSLCreateContext for iOS. 693 | * This is optional for iOS. If not supplied, a NO value is the default. 694 | * This is not needed for Mac OS X, and the value is ignored. 695 | * 696 | * - GCDAsyncSocketSSLPeerID 697 | * The value must be of type NSData. 698 | * You must set this value if you want to use TLS session resumption. 699 | * See Apple's documentation for SSLSetPeerID. 700 | * 701 | * - GCDAsyncSocketSSLProtocolVersionMin 702 | * - GCDAsyncSocketSSLProtocolVersionMax 703 | * The value(s) must be of type NSNumber, encapsulting a SSLProtocol value. 704 | * See Apple's documentation for SSLSetProtocolVersionMin & SSLSetProtocolVersionMax. 705 | * See also the SSLProtocol typedef. 706 | * 707 | * - GCDAsyncSocketSSLSessionOptionFalseStart 708 | * The value must be of type NSNumber, encapsulating a BOOL value. 709 | * See Apple's documentation for kSSLSessionOptionFalseStart. 710 | * 711 | * - GCDAsyncSocketSSLSessionOptionSendOneByteRecord 712 | * The value must be of type NSNumber, encapsulating a BOOL value. 713 | * See Apple's documentation for kSSLSessionOptionSendOneByteRecord. 714 | * 715 | * - GCDAsyncSocketSSLCipherSuites 716 | * The values must be of type NSArray. 717 | * Each item within the array must be a NSNumber, encapsulating 718 | * See Apple's documentation for SSLSetEnabledCiphers. 719 | * See also the SSLCipherSuite typedef. 720 | * 721 | * - GCDAsyncSocketSSLDiffieHellmanParameters (Mac OS X only) 722 | * The value must be of type NSData. 723 | * See Apple's documentation for SSLSetDiffieHellmanParams. 724 | * 725 | * ==== The following UNAVAILABLE KEYS are: (with throw an exception) 726 | * 727 | * - kCFStreamSSLAllowsAnyRoot (UNAVAILABLE) 728 | * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust). 729 | * Corresponding deprecated method: SSLSetAllowsAnyRoot 730 | * 731 | * - kCFStreamSSLAllowsExpiredRoots (UNAVAILABLE) 732 | * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust). 733 | * Corresponding deprecated method: SSLSetAllowsExpiredRoots 734 | * 735 | * - kCFStreamSSLAllowsExpiredCertificates (UNAVAILABLE) 736 | * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust). 737 | * Corresponding deprecated method: SSLSetAllowsExpiredCerts 738 | * 739 | * - kCFStreamSSLValidatesCertificateChain (UNAVAILABLE) 740 | * You MUST use manual trust evaluation instead (see GCDAsyncSocketManuallyEvaluateTrust). 741 | * Corresponding deprecated method: SSLSetEnableCertVerify 742 | * 743 | * - kCFStreamSSLLevel (UNAVAILABLE) 744 | * You MUST use GCDAsyncSocketSSLProtocolVersionMin & GCDAsyncSocketSSLProtocolVersionMin instead. 745 | * Corresponding deprecated method: SSLSetProtocolVersionEnabled 746 | * 747 | * 748 | * Please refer to Apple's documentation for corresponding SSLFunctions. 749 | * 750 | * If you pass in nil or an empty dictionary, the default settings will be used. 751 | * 752 | * IMPORTANT SECURITY NOTE: 753 | * The default settings will check to make sure the remote party's certificate is signed by a 754 | * trusted 3rd party certificate agency (e.g. verisign) and that the certificate is not expired. 755 | * However it will not verify the name on the certificate unless you 756 | * give it a name to verify against via the kCFStreamSSLPeerName key. 757 | * The security implications of this are important to understand. 758 | * Imagine you are attempting to create a secure connection to MySecureServer.com, 759 | * but your socket gets directed to MaliciousServer.com because of a hacked DNS server. 760 | * If you simply use the default settings, and MaliciousServer.com has a valid certificate, 761 | * the default settings will not detect any problems since the certificate is valid. 762 | * To properly secure your connection in this particular scenario you 763 | * should set the kCFStreamSSLPeerName property to "MySecureServer.com". 764 | * 765 | * You can also perform additional validation in socketDidSecure. 766 | **/ 767 | - (void)startTLS:(NSDictionary *)tlsSettings; 768 | 769 | #pragma mark Advanced 770 | 771 | /** 772 | * Traditionally sockets are not closed until the conversation is over. 773 | * However, it is technically possible for the remote enpoint to close its write stream. 774 | * Our socket would then be notified that there is no more data to be read, 775 | * but our socket would still be writeable and the remote endpoint could continue to receive our data. 776 | * 777 | * The argument for this confusing functionality stems from the idea that a client could shut down its 778 | * write stream after sending a request to the server, thus notifying the server there are to be no further requests. 779 | * In practice, however, this technique did little to help server developers. 780 | * 781 | * To make matters worse, from a TCP perspective there is no way to tell the difference from a read stream close 782 | * and a full socket close. They both result in the TCP stack receiving a FIN packet. The only way to tell 783 | * is by continuing to write to the socket. If it was only a read stream close, then writes will continue to work. 784 | * Otherwise an error will be occur shortly (when the remote end sends us a RST packet). 785 | * 786 | * In addition to the technical challenges and confusion, many high level socket/stream API's provide 787 | * no support for dealing with the problem. If the read stream is closed, the API immediately declares the 788 | * socket to be closed, and shuts down the write stream as well. In fact, this is what Apple's CFStream API does. 789 | * It might sound like poor design at first, but in fact it simplifies development. 790 | * 791 | * The vast majority of the time if the read stream is closed it's because the remote endpoint closed its socket. 792 | * Thus it actually makes sense to close the socket at this point. 793 | * And in fact this is what most networking developers want and expect to happen. 794 | * However, if you are writing a server that interacts with a plethora of clients, 795 | * you might encounter a client that uses the discouraged technique of shutting down its write stream. 796 | * If this is the case, you can set this property to NO, 797 | * and make use of the socketDidCloseReadStream delegate method. 798 | * 799 | * The default value is YES. 800 | **/ 801 | @property (atomic, assign, readwrite) BOOL autoDisconnectOnClosedReadStream; 802 | 803 | /** 804 | * GCDAsyncSocket maintains thread safety by using an internal serial dispatch_queue. 805 | * In most cases, the instance creates this queue itself. 806 | * However, to allow for maximum flexibility, the internal queue may be passed in the init method. 807 | * This allows for some advanced options such as controlling socket priority via target queues. 808 | * However, when one begins to use target queues like this, they open the door to some specific deadlock issues. 809 | * 810 | * For example, imagine there are 2 queues: 811 | * dispatch_queue_t socketQueue; 812 | * dispatch_queue_t socketTargetQueue; 813 | * 814 | * If you do this (pseudo-code): 815 | * socketQueue.targetQueue = socketTargetQueue; 816 | * 817 | * Then all socketQueue operations will actually get run on the given socketTargetQueue. 818 | * This is fine and works great in most situations. 819 | * But if you run code directly from within the socketTargetQueue that accesses the socket, 820 | * you could potentially get deadlock. Imagine the following code: 821 | * 822 | * - (BOOL)socketHasSomething 823 | * { 824 | * __block BOOL result = NO; 825 | * dispatch_block_t block = ^{ 826 | * result = [self someInternalMethodToBeRunOnlyOnSocketQueue]; 827 | * } 828 | * if (is_executing_on_queue(socketQueue)) 829 | * block(); 830 | * else 831 | * dispatch_sync(socketQueue, block); 832 | * 833 | * return result; 834 | * } 835 | * 836 | * What happens if you call this method from the socketTargetQueue? The result is deadlock. 837 | * This is because the GCD API offers no mechanism to discover a queue's targetQueue. 838 | * Thus we have no idea if our socketQueue is configured with a targetQueue. 839 | * If we had this information, we could easily avoid deadlock. 840 | * But, since these API's are missing or unfeasible, you'll have to explicitly set it. 841 | * 842 | * IF you pass a socketQueue via the init method, 843 | * AND you've configured the passed socketQueue with a targetQueue, 844 | * THEN you should pass the end queue in the target hierarchy. 845 | * 846 | * For example, consider the following queue hierarchy: 847 | * socketQueue -> ipQueue -> moduleQueue 848 | * 849 | * This example demonstrates priority shaping within some server. 850 | * All incoming client connections from the same IP address are executed on the same target queue. 851 | * And all connections for a particular module are executed on the same target queue. 852 | * Thus, the priority of all networking for the entire module can be changed on the fly. 853 | * Additionally, networking traffic from a single IP cannot monopolize the module. 854 | * 855 | * Here's how you would accomplish something like that: 856 | * - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock 857 | * { 858 | * dispatch_queue_t socketQueue = dispatch_queue_create("", NULL); 859 | * dispatch_queue_t ipQueue = [self ipQueueForAddress:address]; 860 | * 861 | * dispatch_set_target_queue(socketQueue, ipQueue); 862 | * dispatch_set_target_queue(iqQueue, moduleQueue); 863 | * 864 | * return socketQueue; 865 | * } 866 | * - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket 867 | * { 868 | * [clientConnections addObject:newSocket]; 869 | * [newSocket markSocketQueueTargetQueue:moduleQueue]; 870 | * } 871 | * 872 | * Note: This workaround is ONLY needed if you intend to execute code directly on the ipQueue or moduleQueue. 873 | * This is often NOT the case, as such queues are used solely for execution shaping. 874 | **/ 875 | - (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreConfiguredTargetQueue; 876 | - (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreviouslyConfiguredTargetQueue; 877 | 878 | /** 879 | * It's not thread-safe to access certain variables from outside the socket's internal queue. 880 | * 881 | * For example, the socket file descriptor. 882 | * File descriptors are simply integers which reference an index in the per-process file table. 883 | * However, when one requests a new file descriptor (by opening a file or socket), 884 | * the file descriptor returned is guaranteed to be the lowest numbered unused descriptor. 885 | * So if we're not careful, the following could be possible: 886 | * 887 | * - Thread A invokes a method which returns the socket's file descriptor. 888 | * - The socket is closed via the socket's internal queue on thread B. 889 | * - Thread C opens a file, and subsequently receives the file descriptor that was previously the socket's FD. 890 | * - Thread A is now accessing/altering the file instead of the socket. 891 | * 892 | * In addition to this, other variables are not actually objects, 893 | * and thus cannot be retained/released or even autoreleased. 894 | * An example is the sslContext, of type SSLContextRef, which is actually a malloc'd struct. 895 | * 896 | * Although there are internal variables that make it difficult to maintain thread-safety, 897 | * it is important to provide access to these variables 898 | * to ensure this class can be used in a wide array of environments. 899 | * This method helps to accomplish this by invoking the current block on the socket's internal queue. 900 | * The methods below can be invoked from within the block to access 901 | * those generally thread-unsafe internal variables in a thread-safe manner. 902 | * The given block will be invoked synchronously on the socket's internal queue. 903 | * 904 | * If you save references to any protected variables and use them outside the block, you do so at your own peril. 905 | **/ 906 | - (void)performBlock:(dispatch_block_t)block; 907 | 908 | /** 909 | * These methods are only available from within the context of a performBlock: invocation. 910 | * See the documentation for the performBlock: method above. 911 | * 912 | * Provides access to the socket's file descriptor(s). 913 | * If the socket is a server socket (is accepting incoming connections), 914 | * it might actually have multiple internal socket file descriptors - one for IPv4 and one for IPv6. 915 | **/ 916 | - (int)socketFD; 917 | - (int)socket4FD; 918 | - (int)socket6FD; 919 | 920 | #if TARGET_OS_IPHONE 921 | 922 | /** 923 | * These methods are only available from within the context of a performBlock: invocation. 924 | * See the documentation for the performBlock: method above. 925 | * 926 | * Provides access to the socket's internal CFReadStream/CFWriteStream. 927 | * 928 | * These streams are only used as workarounds for specific iOS shortcomings: 929 | * 930 | * - Apple has decided to keep the SecureTransport framework private is iOS. 931 | * This means the only supplied way to do SSL/TLS is via CFStream or some other API layered on top of it. 932 | * Thus, in order to provide SSL/TLS support on iOS we are forced to rely on CFStream, 933 | * instead of the preferred and faster and more powerful SecureTransport. 934 | * 935 | * - If a socket doesn't have backgrounding enabled, and that socket is closed while the app is backgrounded, 936 | * Apple only bothers to notify us via the CFStream API. 937 | * The faster and more powerful GCD API isn't notified properly in this case. 938 | * 939 | * See also: (BOOL)enableBackgroundingOnSocket 940 | **/ 941 | - (CFReadStreamRef)readStream; 942 | - (CFWriteStreamRef)writeStream; 943 | 944 | /** 945 | * This method is only available from within the context of a performBlock: invocation. 946 | * See the documentation for the performBlock: method above. 947 | * 948 | * Configures the socket to allow it to operate when the iOS application has been backgrounded. 949 | * In other words, this method creates a read & write stream, and invokes: 950 | * 951 | * CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); 952 | * CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); 953 | * 954 | * Returns YES if successful, NO otherwise. 955 | * 956 | * Note: Apple does not officially support backgrounding server sockets. 957 | * That is, if your socket is accepting incoming connections, Apple does not officially support 958 | * allowing iOS applications to accept incoming connections while an app is backgrounded. 959 | * 960 | * Example usage: 961 | * 962 | * - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port 963 | * { 964 | * [asyncSocket performBlock:^{ 965 | * [asyncSocket enableBackgroundingOnSocket]; 966 | * }]; 967 | * } 968 | **/ 969 | - (BOOL)enableBackgroundingOnSocket; 970 | 971 | #endif 972 | 973 | /** 974 | * This method is only available from within the context of a performBlock: invocation. 975 | * See the documentation for the performBlock: method above. 976 | * 977 | * Provides access to the socket's SSLContext, if SSL/TLS has been started on the socket. 978 | **/ 979 | - (SSLContextRef)sslContext; 980 | 981 | #pragma mark Utilities 982 | 983 | /** 984 | * The address lookup utility used by the class. 985 | * This method is synchronous, so it's recommended you use it on a background thread/queue. 986 | * 987 | * The special strings "localhost" and "loopback" return the loopback address for IPv4 and IPv6. 988 | * 989 | * @returns 990 | * A mutable array with all IPv4 and IPv6 addresses returned by getaddrinfo. 991 | * The addresses are specifically for TCP connections. 992 | * You can filter the addresses, if needed, using the other utility methods provided by the class. 993 | **/ 994 | + (NSMutableArray *)lookupHost:(NSString *)host port:(uint16_t)port error:(NSError **)errPtr; 995 | 996 | /** 997 | * Extracting host and port information from raw address data. 998 | **/ 999 | 1000 | + (NSString *)hostFromAddress:(NSData *)address; 1001 | + (uint16_t)portFromAddress:(NSData *)address; 1002 | 1003 | + (BOOL)isIPv4Address:(NSData *)address; 1004 | + (BOOL)isIPv6Address:(NSData *)address; 1005 | 1006 | + (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr fromAddress:(NSData *)address; 1007 | 1008 | + (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr family:(sa_family_t *)afPtr fromAddress:(NSData *)address; 1009 | 1010 | /** 1011 | * A few common line separators, for use with the readDataToData:... methods. 1012 | **/ 1013 | + (NSData *)CRLFData; // 0x0D0A 1014 | + (NSData *)CRData; // 0x0D 1015 | + (NSData *)LFData; // 0x0A 1016 | + (NSData *)ZeroData; // 0x00 1017 | 1018 | @end 1019 | 1020 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1021 | #pragma mark - 1022 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1023 | 1024 | @protocol GCDAsyncSocketDelegate 1025 | @optional 1026 | 1027 | /** 1028 | * This method is called immediately prior to socket:didAcceptNewSocket:. 1029 | * It optionally allows a listening socket to specify the socketQueue for a new accepted socket. 1030 | * If this method is not implemented, or returns NULL, the new accepted socket will create its own default queue. 1031 | * 1032 | * Since you cannot autorelease a dispatch_queue, 1033 | * this method uses the "new" prefix in its name to specify that the returned queue has been retained. 1034 | * 1035 | * Thus you could do something like this in the implementation: 1036 | * return dispatch_queue_create("MyQueue", NULL); 1037 | * 1038 | * If you are placing multiple sockets on the same queue, 1039 | * then care should be taken to increment the retain count each time this method is invoked. 1040 | * 1041 | * For example, your implementation might look something like this: 1042 | * dispatch_retain(myExistingQueue); 1043 | * return myExistingQueue; 1044 | **/ 1045 | - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock; 1046 | 1047 | /** 1048 | * Called when a socket accepts a connection. 1049 | * Another socket is automatically spawned to handle it. 1050 | * 1051 | * You must retain the newSocket if you wish to handle the connection. 1052 | * Otherwise the newSocket instance will be released and the spawned connection will be closed. 1053 | * 1054 | * By default the new socket will have the same delegate and delegateQueue. 1055 | * You may, of course, change this at any time. 1056 | **/ 1057 | - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket; 1058 | 1059 | /** 1060 | * Called when a socket connects and is ready for reading and writing. 1061 | * The host parameter will be an IP address, not a DNS name. 1062 | **/ 1063 | - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port; 1064 | 1065 | /** 1066 | * Called when a socket has completed reading the requested data into memory. 1067 | * Not called if there is an error. 1068 | **/ 1069 | - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag; 1070 | 1071 | /** 1072 | * Called when a socket has read in data, but has not yet completed the read. 1073 | * This would occur if using readToData: or readToLength: methods. 1074 | * It may be used to for things such as updating progress bars. 1075 | **/ 1076 | - (void)socket:(GCDAsyncSocket *)sock didReadPartialDataOfLength:(NSUInteger)partialLength tag:(long)tag; 1077 | 1078 | /** 1079 | * Called when a socket has completed writing the requested data. Not called if there is an error. 1080 | **/ 1081 | - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag; 1082 | 1083 | /** 1084 | * Called when a socket has written some data, but has not yet completed the entire write. 1085 | * It may be used to for things such as updating progress bars. 1086 | **/ 1087 | - (void)socket:(GCDAsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag; 1088 | 1089 | /** 1090 | * Called if a read operation has reached its timeout without completing. 1091 | * This method allows you to optionally extend the timeout. 1092 | * If you return a positive time interval (> 0) the read's timeout will be extended by the given amount. 1093 | * If you don't implement this method, or return a non-positive time interval (<= 0) the read will timeout as usual. 1094 | * 1095 | * The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method. 1096 | * The length parameter is the number of bytes that have been read so far for the read operation. 1097 | * 1098 | * Note that this method may be called multiple times for a single read if you return positive numbers. 1099 | **/ 1100 | - (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag 1101 | elapsed:(NSTimeInterval)elapsed 1102 | bytesDone:(NSUInteger)length; 1103 | 1104 | /** 1105 | * Called if a write operation has reached its timeout without completing. 1106 | * This method allows you to optionally extend the timeout. 1107 | * If you return a positive time interval (> 0) the write's timeout will be extended by the given amount. 1108 | * If you don't implement this method, or return a non-positive time interval (<= 0) the write will timeout as usual. 1109 | * 1110 | * The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method. 1111 | * The length parameter is the number of bytes that have been written so far for the write operation. 1112 | * 1113 | * Note that this method may be called multiple times for a single write if you return positive numbers. 1114 | **/ 1115 | - (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag 1116 | elapsed:(NSTimeInterval)elapsed 1117 | bytesDone:(NSUInteger)length; 1118 | 1119 | /** 1120 | * Conditionally called if the read stream closes, but the write stream may still be writeable. 1121 | * 1122 | * This delegate method is only called if autoDisconnectOnClosedReadStream has been set to NO. 1123 | * See the discussion on the autoDisconnectOnClosedReadStream method for more information. 1124 | **/ 1125 | - (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock; 1126 | 1127 | /** 1128 | * Called when a socket disconnects with or without error. 1129 | * 1130 | * If you call the disconnect method, and the socket wasn't already disconnected, 1131 | * then an invocation of this delegate method will be enqueued on the delegateQueue 1132 | * before the disconnect method returns. 1133 | * 1134 | * Note: If the GCDAsyncSocket instance is deallocated while it is still connected, 1135 | * and the delegate is not also deallocated, then this method will be invoked, 1136 | * but the sock parameter will be nil. (It must necessarily be nil since it is no longer available.) 1137 | * This is a generally rare, but is possible if one writes code like this: 1138 | * 1139 | * asyncSocket = nil; // I'm implicitly disconnecting the socket 1140 | * 1141 | * In this case it may preferrable to nil the delegate beforehand, like this: 1142 | * 1143 | * asyncSocket.delegate = nil; // Don't invoke my delegate method 1144 | * asyncSocket = nil; // I'm implicitly disconnecting the socket 1145 | * 1146 | * Of course, this depends on how your state machine is configured. 1147 | **/ 1148 | - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err; 1149 | 1150 | /** 1151 | * Called after the socket has successfully completed SSL/TLS negotiation. 1152 | * This method is not called unless you use the provided startTLS method. 1153 | * 1154 | * If a SSL/TLS negotiation fails (invalid certificate, etc) then the socket will immediately close, 1155 | * and the socketDidDisconnect:withError: delegate method will be called with the specific SSL error code. 1156 | **/ 1157 | - (void)socketDidSecure:(GCDAsyncSocket *)sock; 1158 | 1159 | /** 1160 | * Allows a socket delegate to hook into the TLS handshake and manually validate the peer it's connecting to. 1161 | * 1162 | * This is only called if startTLS is invoked with options that include: 1163 | * - GCDAsyncSocketManuallyEvaluateTrust == YES 1164 | * 1165 | * Typically the delegate will use SecTrustEvaluate (and related functions) to properly validate the peer. 1166 | * 1167 | * Note from Apple's documentation: 1168 | * Because [SecTrustEvaluate] might look on the network for certificates in the certificate chain, 1169 | * [it] might block while attempting network access. You should never call it from your main thread; 1170 | * call it only from within a function running on a dispatch queue or on a separate thread. 1171 | * 1172 | * Thus this method uses a completionHandler block rather than a normal return value. 1173 | * The completionHandler block is thread-safe, and may be invoked from a background queue/thread. 1174 | * It is safe to invoke the completionHandler block even if the socket has been closed. 1175 | **/ 1176 | - (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust 1177 | completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler; 1178 | 1179 | @end 1180 | -------------------------------------------------------------------------------- /AnyBar/GCDAsyncUdpSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // GCDAsyncUdpSocket 3 | // 4 | // This class is in the public domain. 5 | // Originally created by Robbie Hanson of Deusty LLC. 6 | // Updated and maintained by Deusty LLC and the Apple development community. 7 | // 8 | // https://github.com/robbiehanson/CocoaAsyncSocket 9 | // 10 | 11 | #import 12 | #import 13 | #import 14 | #import 15 | 16 | extern NSString *const GCDAsyncUdpSocketException; 17 | extern NSString *const GCDAsyncUdpSocketErrorDomain; 18 | 19 | extern NSString *const GCDAsyncUdpSocketQueueName; 20 | extern NSString *const GCDAsyncUdpSocketThreadName; 21 | 22 | enum GCDAsyncUdpSocketError 23 | { 24 | GCDAsyncUdpSocketNoError = 0, // Never used 25 | GCDAsyncUdpSocketBadConfigError, // Invalid configuration 26 | GCDAsyncUdpSocketBadParamError, // Invalid parameter was passed 27 | GCDAsyncUdpSocketSendTimeoutError, // A send operation timed out 28 | GCDAsyncUdpSocketClosedError, // The socket was closed 29 | GCDAsyncUdpSocketOtherError, // Description provided in userInfo 30 | }; 31 | typedef enum GCDAsyncUdpSocketError GCDAsyncUdpSocketError; 32 | 33 | /** 34 | * You may optionally set a receive filter for the socket. 35 | * A filter can provide several useful features: 36 | * 37 | * 1. Many times udp packets need to be parsed. 38 | * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. 39 | * The end result is a parallel socket io, datagram parsing, and packet processing. 40 | * 41 | * 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. 42 | * The filter can prevent such packets from arriving at the delegate. 43 | * And because the filter can run in its own independent queue, this doesn't slow down the delegate. 44 | * 45 | * - Since the udp protocol does not guarantee delivery, udp packets may be lost. 46 | * Many protocols built atop udp thus provide various resend/re-request algorithms. 47 | * This sometimes results in duplicate packets arriving. 48 | * A filter may allow you to architect the duplicate detection code to run in parallel to normal processing. 49 | * 50 | * - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive. 51 | * Such packets need to be ignored. 52 | * 53 | * 3. Sometimes traffic shapers are needed to simulate real world environments. 54 | * A filter allows you to write custom code to simulate such environments. 55 | * The ability to code this yourself is especially helpful when your simulated environment 56 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), 57 | * or the system tools to handle this aren't available (e.g. on a mobile device). 58 | * 59 | * @param data - The packet that was received. 60 | * @param address - The address the data was received from. 61 | * See utilities section for methods to extract info from address. 62 | * @param context - Out parameter you may optionally set, which will then be passed to the delegate method. 63 | * For example, filter block can parse the data and then, 64 | * pass the parsed data to the delegate. 65 | * 66 | * @returns - YES if the received packet should be passed onto the delegate. 67 | * NO if the received packet should be discarded, and not reported to the delegete. 68 | * 69 | * Example: 70 | * 71 | * GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) { 72 | * 73 | * MyProtocolMessage *msg = [MyProtocol parseMessage:data]; 74 | * 75 | * *context = response; 76 | * return (response != nil); 77 | * }; 78 | * [udpSocket setReceiveFilter:filter withQueue:myParsingQueue]; 79 | * 80 | **/ 81 | typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *address, id *context); 82 | 83 | /** 84 | * You may optionally set a send filter for the socket. 85 | * A filter can provide several interesting possibilities: 86 | * 87 | * 1. Optional caching of resolved addresses for domain names. 88 | * The cache could later be consulted, resulting in fewer system calls to getaddrinfo. 89 | * 90 | * 2. Reusable modules of code for bandwidth monitoring. 91 | * 92 | * 3. Sometimes traffic shapers are needed to simulate real world environments. 93 | * A filter allows you to write custom code to simulate such environments. 94 | * The ability to code this yourself is especially helpful when your simulated environment 95 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), 96 | * or the system tools to handle this aren't available (e.g. on a mobile device). 97 | * 98 | * @param data - The packet that was received. 99 | * @param address - The address the data was received from. 100 | * See utilities section for methods to extract info from address. 101 | * @param tag - The tag that was passed in the send method. 102 | * 103 | * @returns - YES if the packet should actually be sent over the socket. 104 | * NO if the packet should be silently dropped (not sent over the socket). 105 | * 106 | * Regardless of the return value, the delegate will be informed that the packet was successfully sent. 107 | * 108 | **/ 109 | typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, long tag); 110 | 111 | 112 | @interface GCDAsyncUdpSocket : NSObject 113 | 114 | /** 115 | * GCDAsyncUdpSocket uses the standard delegate paradigm, 116 | * but executes all delegate callbacks on a given delegate dispatch queue. 117 | * This allows for maximum concurrency, while at the same time providing easy thread safety. 118 | * 119 | * You MUST set a delegate AND delegate dispatch queue before attempting to 120 | * use the socket, or you will get an error. 121 | * 122 | * The socket queue is optional. 123 | * If you pass NULL, GCDAsyncSocket will automatically create its own socket queue. 124 | * If you choose to provide a socket queue, the socket queue must not be a concurrent queue, 125 | * then please see the discussion for the method markSocketQueueTargetQueue. 126 | * 127 | * The delegate queue and socket queue can optionally be the same. 128 | **/ 129 | - (id)init; 130 | - (id)initWithSocketQueue:(dispatch_queue_t)sq; 131 | - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq; 132 | - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq; 133 | 134 | #pragma mark Configuration 135 | 136 | - (id)delegate; 137 | - (void)setDelegate:(id)delegate; 138 | - (void)synchronouslySetDelegate:(id)delegate; 139 | 140 | - (dispatch_queue_t)delegateQueue; 141 | - (void)setDelegateQueue:(dispatch_queue_t)delegateQueue; 142 | - (void)synchronouslySetDelegateQueue:(dispatch_queue_t)delegateQueue; 143 | 144 | - (void)getDelegate:(id *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr; 145 | - (void)setDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue; 146 | - (void)synchronouslySetDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue; 147 | 148 | /** 149 | * By default, both IPv4 and IPv6 are enabled. 150 | * 151 | * This means GCDAsyncUdpSocket automatically supports both protocols, 152 | * and can send to IPv4 or IPv6 addresses, 153 | * as well as receive over IPv4 and IPv6. 154 | * 155 | * For operations that require DNS resolution, GCDAsyncUdpSocket supports both IPv4 and IPv6. 156 | * If a DNS lookup returns only IPv4 results, GCDAsyncUdpSocket will automatically use IPv4. 157 | * If a DNS lookup returns only IPv6 results, GCDAsyncUdpSocket will automatically use IPv6. 158 | * If a DNS lookup returns both IPv4 and IPv6 results, then the protocol used depends on the configured preference. 159 | * If IPv4 is preferred, then IPv4 is used. 160 | * If IPv6 is preferred, then IPv6 is used. 161 | * If neutral, then the first IP version in the resolved array will be used. 162 | * 163 | * Starting with Mac OS X 10.7 Lion and iOS 5, the default IP preference is neutral. 164 | * On prior systems the default IP preference is IPv4. 165 | **/ 166 | - (BOOL)isIPv4Enabled; 167 | - (void)setIPv4Enabled:(BOOL)flag; 168 | 169 | - (BOOL)isIPv6Enabled; 170 | - (void)setIPv6Enabled:(BOOL)flag; 171 | 172 | - (BOOL)isIPv4Preferred; 173 | - (BOOL)isIPv6Preferred; 174 | - (BOOL)isIPVersionNeutral; 175 | 176 | - (void)setPreferIPv4; 177 | - (void)setPreferIPv6; 178 | - (void)setIPVersionNeutral; 179 | 180 | /** 181 | * Gets/Sets the maximum size of the buffer that will be allocated for receive operations. 182 | * The default maximum size is 9216 bytes. 183 | * 184 | * The theoretical maximum size of any IPv4 UDP packet is UINT16_MAX = 65535. 185 | * The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295. 186 | * 187 | * Since the OS/GCD notifies us of the size of each received UDP packet, 188 | * the actual allocated buffer size for each packet is exact. 189 | * And in practice the size of UDP packets is generally much smaller than the max. 190 | * Indeed most protocols will send and receive packets of only a few bytes, 191 | * or will set a limit on the size of packets to prevent fragmentation in the IP layer. 192 | * 193 | * If you set the buffer size too small, the sockets API in the OS will silently discard 194 | * any extra data, and you will not be notified of the error. 195 | **/ 196 | - (uint16_t)maxReceiveIPv4BufferSize; 197 | - (void)setMaxReceiveIPv4BufferSize:(uint16_t)max; 198 | 199 | - (uint32_t)maxReceiveIPv6BufferSize; 200 | - (void)setMaxReceiveIPv6BufferSize:(uint32_t)max; 201 | 202 | /** 203 | * User data allows you to associate arbitrary information with the socket. 204 | * This data is not used internally in any way. 205 | **/ 206 | - (id)userData; 207 | - (void)setUserData:(id)arbitraryUserData; 208 | 209 | #pragma mark Diagnostics 210 | 211 | /** 212 | * Returns the local address info for the socket. 213 | * 214 | * The localAddress method returns a sockaddr structure wrapped in a NSData object. 215 | * The localHost method returns the human readable IP address as a string. 216 | * 217 | * Note: Address info may not be available until after the socket has been binded, connected 218 | * or until after data has been sent. 219 | **/ 220 | - (NSData *)localAddress; 221 | - (NSString *)localHost; 222 | - (uint16_t)localPort; 223 | 224 | - (NSData *)localAddress_IPv4; 225 | - (NSString *)localHost_IPv4; 226 | - (uint16_t)localPort_IPv4; 227 | 228 | - (NSData *)localAddress_IPv6; 229 | - (NSString *)localHost_IPv6; 230 | - (uint16_t)localPort_IPv6; 231 | 232 | /** 233 | * Returns the remote address info for the socket. 234 | * 235 | * The connectedAddress method returns a sockaddr structure wrapped in a NSData object. 236 | * The connectedHost method returns the human readable IP address as a string. 237 | * 238 | * Note: Since UDP is connectionless by design, connected address info 239 | * will not be available unless the socket is explicitly connected to a remote host/port. 240 | * If the socket is not connected, these methods will return nil / 0. 241 | **/ 242 | - (NSData *)connectedAddress; 243 | - (NSString *)connectedHost; 244 | - (uint16_t)connectedPort; 245 | 246 | /** 247 | * Returns whether or not this socket has been connected to a single host. 248 | * By design, UDP is a connectionless protocol, and connecting is not needed. 249 | * If connected, the socket will only be able to send/receive data to/from the connected host. 250 | **/ 251 | - (BOOL)isConnected; 252 | 253 | /** 254 | * Returns whether or not this socket has been closed. 255 | * The only way a socket can be closed is if you explicitly call one of the close methods. 256 | **/ 257 | - (BOOL)isClosed; 258 | 259 | /** 260 | * Returns whether or not this socket is IPv4. 261 | * 262 | * By default this will be true, unless: 263 | * - IPv4 is disabled (via setIPv4Enabled:) 264 | * - The socket is explicitly bound to an IPv6 address 265 | * - The socket is connected to an IPv6 address 266 | **/ 267 | - (BOOL)isIPv4; 268 | 269 | /** 270 | * Returns whether or not this socket is IPv6. 271 | * 272 | * By default this will be true, unless: 273 | * - IPv6 is disabled (via setIPv6Enabled:) 274 | * - The socket is explicitly bound to an IPv4 address 275 | * _ The socket is connected to an IPv4 address 276 | * 277 | * This method will also return false on platforms that do not support IPv6. 278 | * Note: The iPhone does not currently support IPv6. 279 | **/ 280 | - (BOOL)isIPv6; 281 | 282 | #pragma mark Binding 283 | 284 | /** 285 | * Binds the UDP socket to the given port. 286 | * Binding should be done for server sockets that receive data prior to sending it. 287 | * Client sockets can skip binding, 288 | * as the OS will automatically assign the socket an available port when it starts sending data. 289 | * 290 | * You may optionally pass a port number of zero to immediately bind the socket, 291 | * yet still allow the OS to automatically assign an available port. 292 | * 293 | * You cannot bind a socket after its been connected. 294 | * You can only bind a socket once. 295 | * You can still connect a socket (if desired) after binding. 296 | * 297 | * On success, returns YES. 298 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. 299 | **/ 300 | - (BOOL)bindToPort:(uint16_t)port error:(NSError **)errPtr; 301 | 302 | /** 303 | * Binds the UDP socket to the given port and optional interface. 304 | * Binding should be done for server sockets that receive data prior to sending it. 305 | * Client sockets can skip binding, 306 | * as the OS will automatically assign the socket an available port when it starts sending data. 307 | * 308 | * You may optionally pass a port number of zero to immediately bind the socket, 309 | * yet still allow the OS to automatically assign an available port. 310 | * 311 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). 312 | * You may also use the special strings "localhost" or "loopback" to specify that 313 | * the socket only accept packets from the local machine. 314 | * 315 | * You cannot bind a socket after its been connected. 316 | * You can only bind a socket once. 317 | * You can still connect a socket (if desired) after binding. 318 | * 319 | * On success, returns YES. 320 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. 321 | **/ 322 | - (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError **)errPtr; 323 | 324 | /** 325 | * Binds the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object. 326 | * 327 | * If you have an existing struct sockaddr you can convert it to a NSData object like so: 328 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; 329 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; 330 | * 331 | * Binding should be done for server sockets that receive data prior to sending it. 332 | * Client sockets can skip binding, 333 | * as the OS will automatically assign the socket an available port when it starts sending data. 334 | * 335 | * You cannot bind a socket after its been connected. 336 | * You can only bind a socket once. 337 | * You can still connect a socket (if desired) after binding. 338 | * 339 | * On success, returns YES. 340 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. 341 | **/ 342 | - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr; 343 | 344 | #pragma mark Connecting 345 | 346 | /** 347 | * Connects the UDP socket to the given host and port. 348 | * By design, UDP is a connectionless protocol, and connecting is not needed. 349 | * 350 | * Choosing to connect to a specific host/port has the following effect: 351 | * - You will only be able to send data to the connected host/port. 352 | * - You will only be able to receive data from the connected host/port. 353 | * - You will receive ICMP messages that come from the connected host/port, such as "connection refused". 354 | * 355 | * The actual process of connecting a UDP socket does not result in any communication on the socket. 356 | * It simply changes the internal state of the socket. 357 | * 358 | * You cannot bind a socket after it has been connected. 359 | * You can only connect a socket once. 360 | * 361 | * The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). 362 | * 363 | * This method is asynchronous as it requires a DNS lookup to resolve the given host name. 364 | * If an obvious error is detected, this method immediately returns NO and sets errPtr. 365 | * If you don't care about the error, you can pass nil for errPtr. 366 | * Otherwise, this method returns YES and begins the asynchronous connection process. 367 | * The result of the asynchronous connection process will be reported via the delegate methods. 368 | **/ 369 | - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr; 370 | 371 | /** 372 | * Connects the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object. 373 | * 374 | * If you have an existing struct sockaddr you can convert it to a NSData object like so: 375 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; 376 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; 377 | * 378 | * By design, UDP is a connectionless protocol, and connecting is not needed. 379 | * 380 | * Choosing to connect to a specific address has the following effect: 381 | * - You will only be able to send data to the connected address. 382 | * - You will only be able to receive data from the connected address. 383 | * - You will receive ICMP messages that come from the connected address, such as "connection refused". 384 | * 385 | * Connecting a UDP socket does not result in any communication on the socket. 386 | * It simply changes the internal state of the socket. 387 | * 388 | * You cannot bind a socket after its been connected. 389 | * You can only connect a socket once. 390 | * 391 | * On success, returns YES. 392 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. 393 | * 394 | * Note: Unlike the connectToHost:onPort:error: method, this method does not require a DNS lookup. 395 | * Thus when this method returns, the connection has either failed or fully completed. 396 | * In other words, this method is synchronous, unlike the asynchronous connectToHost::: method. 397 | * However, for compatibility and simplification of delegate code, if this method returns YES 398 | * then the corresponding delegate method (udpSocket:didConnectToHost:port:) is still invoked. 399 | **/ 400 | - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; 401 | 402 | #pragma mark Multicast 403 | 404 | /** 405 | * Join multicast group. 406 | * Group should be an IP address (eg @"225.228.0.1"). 407 | * 408 | * On success, returns YES. 409 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. 410 | **/ 411 | - (BOOL)joinMulticastGroup:(NSString *)group error:(NSError **)errPtr; 412 | 413 | /** 414 | * Join multicast group. 415 | * Group should be an IP address (eg @"225.228.0.1"). 416 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). 417 | * 418 | * On success, returns YES. 419 | * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. 420 | **/ 421 | - (BOOL)joinMulticastGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr; 422 | 423 | - (BOOL)leaveMulticastGroup:(NSString *)group error:(NSError **)errPtr; 424 | - (BOOL)leaveMulticastGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr; 425 | 426 | #pragma mark Broadcast 427 | 428 | /** 429 | * By default, the underlying socket in the OS will not allow you to send broadcast messages. 430 | * In order to send broadcast messages, you need to enable this functionality in the socket. 431 | * 432 | * A broadcast is a UDP message to addresses like "192.168.255.255" or "255.255.255.255" that is 433 | * delivered to every host on the network. 434 | * The reason this is generally disabled by default (by the OS) is to prevent 435 | * accidental broadcast messages from flooding the network. 436 | **/ 437 | - (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr; 438 | 439 | #pragma mark Sending 440 | 441 | /** 442 | * Asynchronously sends the given data, with the given timeout and tag. 443 | * 444 | * This method may only be used with a connected socket. 445 | * Recall that connecting is optional for a UDP socket. 446 | * For connected sockets, data can only be sent to the connected address. 447 | * For non-connected sockets, the remote destination is specified for each packet. 448 | * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. 449 | * 450 | * @param data 451 | * The data to send. 452 | * If data is nil or zero-length, this method does nothing. 453 | * If passing NSMutableData, please read the thread-safety notice below. 454 | * 455 | * @param timeout 456 | * The timeout for the send opeartion. 457 | * If the timeout value is negative, the send operation will not use a timeout. 458 | * 459 | * @param tag 460 | * The tag is for your convenience. 461 | * It is not sent or received over the socket in any manner what-so-ever. 462 | * It is reported back as a parameter in the udpSocket:didSendDataWithTag: 463 | * or udpSocket:didNotSendDataWithTag:dueToError: methods. 464 | * You can use it as an array index, state id, type constant, etc. 465 | * 466 | * 467 | * Thread-Safety Note: 468 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while 469 | * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method 470 | * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying 471 | * that this particular send operation has completed. 472 | * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. 473 | * It simply retains it for performance reasons. 474 | * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. 475 | * Copying this data adds an unwanted/unneeded overhead. 476 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket 477 | * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time 478 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. 479 | **/ 480 | - (void)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; 481 | 482 | /** 483 | * Asynchronously sends the given data, with the given timeout and tag, to the given host and port. 484 | * 485 | * This method cannot be used with a connected socket. 486 | * Recall that connecting is optional for a UDP socket. 487 | * For connected sockets, data can only be sent to the connected address. 488 | * For non-connected sockets, the remote destination is specified for each packet. 489 | * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. 490 | * 491 | * @param data 492 | * The data to send. 493 | * If data is nil or zero-length, this method does nothing. 494 | * If passing NSMutableData, please read the thread-safety notice below. 495 | * 496 | * @param host 497 | * The destination to send the udp packet to. 498 | * May be specified as a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). 499 | * You may also use the convenience strings of "loopback" or "localhost". 500 | * 501 | * @param port 502 | * The port of the host to send to. 503 | * 504 | * @param timeout 505 | * The timeout for the send opeartion. 506 | * If the timeout value is negative, the send operation will not use a timeout. 507 | * 508 | * @param tag 509 | * The tag is for your convenience. 510 | * It is not sent or received over the socket in any manner what-so-ever. 511 | * It is reported back as a parameter in the udpSocket:didSendDataWithTag: 512 | * or udpSocket:didNotSendDataWithTag:dueToError: methods. 513 | * You can use it as an array index, state id, type constant, etc. 514 | * 515 | * 516 | * Thread-Safety Note: 517 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while 518 | * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method 519 | * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying 520 | * that this particular send operation has completed. 521 | * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. 522 | * It simply retains it for performance reasons. 523 | * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. 524 | * Copying this data adds an unwanted/unneeded overhead. 525 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket 526 | * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time 527 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. 528 | **/ 529 | - (void)sendData:(NSData *)data 530 | toHost:(NSString *)host 531 | port:(uint16_t)port 532 | withTimeout:(NSTimeInterval)timeout 533 | tag:(long)tag; 534 | 535 | /** 536 | * Asynchronously sends the given data, with the given timeout and tag, to the given address. 537 | * 538 | * This method cannot be used with a connected socket. 539 | * Recall that connecting is optional for a UDP socket. 540 | * For connected sockets, data can only be sent to the connected address. 541 | * For non-connected sockets, the remote destination is specified for each packet. 542 | * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. 543 | * 544 | * @param data 545 | * The data to send. 546 | * If data is nil or zero-length, this method does nothing. 547 | * If passing NSMutableData, please read the thread-safety notice below. 548 | * 549 | * @param remoteAddr 550 | * The address to send the data to (specified as a sockaddr structure wrapped in a NSData object). 551 | * 552 | * @param timeout 553 | * The timeout for the send opeartion. 554 | * If the timeout value is negative, the send operation will not use a timeout. 555 | * 556 | * @param tag 557 | * The tag is for your convenience. 558 | * It is not sent or received over the socket in any manner what-so-ever. 559 | * It is reported back as a parameter in the udpSocket:didSendDataWithTag: 560 | * or udpSocket:didNotSendDataWithTag:dueToError: methods. 561 | * You can use it as an array index, state id, type constant, etc. 562 | * 563 | * 564 | * Thread-Safety Note: 565 | * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while 566 | * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method 567 | * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying 568 | * that this particular send operation has completed. 569 | * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. 570 | * It simply retains it for performance reasons. 571 | * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. 572 | * Copying this data adds an unwanted/unneeded overhead. 573 | * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket 574 | * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time 575 | * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. 576 | **/ 577 | - (void)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag; 578 | 579 | /** 580 | * You may optionally set a send filter for the socket. 581 | * A filter can provide several interesting possibilities: 582 | * 583 | * 1. Optional caching of resolved addresses for domain names. 584 | * The cache could later be consulted, resulting in fewer system calls to getaddrinfo. 585 | * 586 | * 2. Reusable modules of code for bandwidth monitoring. 587 | * 588 | * 3. Sometimes traffic shapers are needed to simulate real world environments. 589 | * A filter allows you to write custom code to simulate such environments. 590 | * The ability to code this yourself is especially helpful when your simulated environment 591 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), 592 | * or the system tools to handle this aren't available (e.g. on a mobile device). 593 | * 594 | * For more information about GCDAsyncUdpSocketSendFilterBlock, see the documentation for its typedef. 595 | * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue. 596 | * 597 | * Note: This method invokes setSendFilter:withQueue:isAsynchronous: (documented below), 598 | * passing YES for the isAsynchronous parameter. 599 | **/ 600 | - (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue; 601 | 602 | /** 603 | * The receive filter can be run via dispatch_async or dispatch_sync. 604 | * Most typical situations call for asynchronous operation. 605 | * 606 | * However, there are a few situations in which synchronous operation is preferred. 607 | * Such is the case when the filter is extremely minimal and fast. 608 | * This is because dispatch_sync is faster than dispatch_async. 609 | * 610 | * If you choose synchronous operation, be aware of possible deadlock conditions. 611 | * Since the socket queue is executing your block via dispatch_sync, 612 | * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue. 613 | * For example, you can't query properties on the socket. 614 | **/ 615 | - (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock 616 | withQueue:(dispatch_queue_t)filterQueue 617 | isAsynchronous:(BOOL)isAsynchronous; 618 | 619 | #pragma mark Receiving 620 | 621 | /** 622 | * There are two modes of operation for receiving packets: one-at-a-time & continuous. 623 | * 624 | * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet. 625 | * Receiving packets one-at-a-time may be better suited for implementing certain state machine code, 626 | * where your state machine may not always be ready to process incoming packets. 627 | * 628 | * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received. 629 | * Receiving packets continuously is better suited to real-time streaming applications. 630 | * 631 | * You may switch back and forth between one-at-a-time mode and continuous mode. 632 | * If the socket is currently in continuous mode, calling this method will switch it to one-at-a-time mode. 633 | * 634 | * When a packet is received (and not filtered by the optional receive filter), 635 | * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked. 636 | * 637 | * If the socket is able to begin receiving packets, this method returns YES. 638 | * Otherwise it returns NO, and sets the errPtr with appropriate error information. 639 | * 640 | * An example error: 641 | * You created a udp socket to act as a server, and immediately called receive. 642 | * You forgot to first bind the socket to a port number, and received a error with a message like: 643 | * "Must bind socket before you can receive data." 644 | **/ 645 | - (BOOL)receiveOnce:(NSError **)errPtr; 646 | 647 | /** 648 | * There are two modes of operation for receiving packets: one-at-a-time & continuous. 649 | * 650 | * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet. 651 | * Receiving packets one-at-a-time may be better suited for implementing certain state machine code, 652 | * where your state machine may not always be ready to process incoming packets. 653 | * 654 | * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received. 655 | * Receiving packets continuously is better suited to real-time streaming applications. 656 | * 657 | * You may switch back and forth between one-at-a-time mode and continuous mode. 658 | * If the socket is currently in one-at-a-time mode, calling this method will switch it to continuous mode. 659 | * 660 | * For every received packet (not filtered by the optional receive filter), 661 | * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked. 662 | * 663 | * If the socket is able to begin receiving packets, this method returns YES. 664 | * Otherwise it returns NO, and sets the errPtr with appropriate error information. 665 | * 666 | * An example error: 667 | * You created a udp socket to act as a server, and immediately called receive. 668 | * You forgot to first bind the socket to a port number, and received a error with a message like: 669 | * "Must bind socket before you can receive data." 670 | **/ 671 | - (BOOL)beginReceiving:(NSError **)errPtr; 672 | 673 | /** 674 | * If the socket is currently receiving (beginReceiving has been called), this method pauses the receiving. 675 | * That is, it won't read any more packets from the underlying OS socket until beginReceiving is called again. 676 | * 677 | * Important Note: 678 | * GCDAsyncUdpSocket may be running in parallel with your code. 679 | * That is, your delegate is likely running on a separate thread/dispatch_queue. 680 | * When you invoke this method, GCDAsyncUdpSocket may have already dispatched delegate methods to be invoked. 681 | * Thus, if those delegate methods have already been dispatch_async'd, 682 | * your didReceive delegate method may still be invoked after this method has been called. 683 | * You should be aware of this, and program defensively. 684 | **/ 685 | - (void)pauseReceiving; 686 | 687 | /** 688 | * You may optionally set a receive filter for the socket. 689 | * This receive filter may be set to run in its own queue (independent of delegate queue). 690 | * 691 | * A filter can provide several useful features. 692 | * 693 | * 1. Many times udp packets need to be parsed. 694 | * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. 695 | * The end result is a parallel socket io, datagram parsing, and packet processing. 696 | * 697 | * 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. 698 | * The filter can prevent such packets from arriving at the delegate. 699 | * And because the filter can run in its own independent queue, this doesn't slow down the delegate. 700 | * 701 | * - Since the udp protocol does not guarantee delivery, udp packets may be lost. 702 | * Many protocols built atop udp thus provide various resend/re-request algorithms. 703 | * This sometimes results in duplicate packets arriving. 704 | * A filter may allow you to architect the duplicate detection code to run in parallel to normal processing. 705 | * 706 | * - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive. 707 | * Such packets need to be ignored. 708 | * 709 | * 3. Sometimes traffic shapers are needed to simulate real world environments. 710 | * A filter allows you to write custom code to simulate such environments. 711 | * The ability to code this yourself is especially helpful when your simulated environment 712 | * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), 713 | * or the system tools to handle this aren't available (e.g. on a mobile device). 714 | * 715 | * Example: 716 | * 717 | * GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) { 718 | * 719 | * MyProtocolMessage *msg = [MyProtocol parseMessage:data]; 720 | * 721 | * *context = response; 722 | * return (response != nil); 723 | * }; 724 | * [udpSocket setReceiveFilter:filter withQueue:myParsingQueue]; 725 | * 726 | * For more information about GCDAsyncUdpSocketReceiveFilterBlock, see the documentation for its typedef. 727 | * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue. 728 | * 729 | * Note: This method invokes setReceiveFilter:withQueue:isAsynchronous: (documented below), 730 | * passing YES for the isAsynchronous parameter. 731 | **/ 732 | - (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue; 733 | 734 | /** 735 | * The receive filter can be run via dispatch_async or dispatch_sync. 736 | * Most typical situations call for asynchronous operation. 737 | * 738 | * However, there are a few situations in which synchronous operation is preferred. 739 | * Such is the case when the filter is extremely minimal and fast. 740 | * This is because dispatch_sync is faster than dispatch_async. 741 | * 742 | * If you choose synchronous operation, be aware of possible deadlock conditions. 743 | * Since the socket queue is executing your block via dispatch_sync, 744 | * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue. 745 | * For example, you can't query properties on the socket. 746 | **/ 747 | - (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock 748 | withQueue:(dispatch_queue_t)filterQueue 749 | isAsynchronous:(BOOL)isAsynchronous; 750 | 751 | #pragma mark Closing 752 | 753 | /** 754 | * Immediately closes the underlying socket. 755 | * Any pending send operations are discarded. 756 | * 757 | * The GCDAsyncUdpSocket instance may optionally be used again. 758 | * (it will setup/configure/use another unnderlying BSD socket). 759 | **/ 760 | - (void)close; 761 | 762 | /** 763 | * Closes the underlying socket after all pending send operations have been sent. 764 | * 765 | * The GCDAsyncUdpSocket instance may optionally be used again. 766 | * (it will setup/configure/use another unnderlying BSD socket). 767 | **/ 768 | - (void)closeAfterSending; 769 | 770 | #pragma mark Advanced 771 | /** 772 | * GCDAsyncSocket maintains thread safety by using an internal serial dispatch_queue. 773 | * In most cases, the instance creates this queue itself. 774 | * However, to allow for maximum flexibility, the internal queue may be passed in the init method. 775 | * This allows for some advanced options such as controlling socket priority via target queues. 776 | * However, when one begins to use target queues like this, they open the door to some specific deadlock issues. 777 | * 778 | * For example, imagine there are 2 queues: 779 | * dispatch_queue_t socketQueue; 780 | * dispatch_queue_t socketTargetQueue; 781 | * 782 | * If you do this (pseudo-code): 783 | * socketQueue.targetQueue = socketTargetQueue; 784 | * 785 | * Then all socketQueue operations will actually get run on the given socketTargetQueue. 786 | * This is fine and works great in most situations. 787 | * But if you run code directly from within the socketTargetQueue that accesses the socket, 788 | * you could potentially get deadlock. Imagine the following code: 789 | * 790 | * - (BOOL)socketHasSomething 791 | * { 792 | * __block BOOL result = NO; 793 | * dispatch_block_t block = ^{ 794 | * result = [self someInternalMethodToBeRunOnlyOnSocketQueue]; 795 | * } 796 | * if (is_executing_on_queue(socketQueue)) 797 | * block(); 798 | * else 799 | * dispatch_sync(socketQueue, block); 800 | * 801 | * return result; 802 | * } 803 | * 804 | * What happens if you call this method from the socketTargetQueue? The result is deadlock. 805 | * This is because the GCD API offers no mechanism to discover a queue's targetQueue. 806 | * Thus we have no idea if our socketQueue is configured with a targetQueue. 807 | * If we had this information, we could easily avoid deadlock. 808 | * But, since these API's are missing or unfeasible, you'll have to explicitly set it. 809 | * 810 | * IF you pass a socketQueue via the init method, 811 | * AND you've configured the passed socketQueue with a targetQueue, 812 | * THEN you should pass the end queue in the target hierarchy. 813 | * 814 | * For example, consider the following queue hierarchy: 815 | * socketQueue -> ipQueue -> moduleQueue 816 | * 817 | * This example demonstrates priority shaping within some server. 818 | * All incoming client connections from the same IP address are executed on the same target queue. 819 | * And all connections for a particular module are executed on the same target queue. 820 | * Thus, the priority of all networking for the entire module can be changed on the fly. 821 | * Additionally, networking traffic from a single IP cannot monopolize the module. 822 | * 823 | * Here's how you would accomplish something like that: 824 | * - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock 825 | * { 826 | * dispatch_queue_t socketQueue = dispatch_queue_create("", NULL); 827 | * dispatch_queue_t ipQueue = [self ipQueueForAddress:address]; 828 | * 829 | * dispatch_set_target_queue(socketQueue, ipQueue); 830 | * dispatch_set_target_queue(iqQueue, moduleQueue); 831 | * 832 | * return socketQueue; 833 | * } 834 | * - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket 835 | * { 836 | * [clientConnections addObject:newSocket]; 837 | * [newSocket markSocketQueueTargetQueue:moduleQueue]; 838 | * } 839 | * 840 | * Note: This workaround is ONLY needed if you intend to execute code directly on the ipQueue or moduleQueue. 841 | * This is often NOT the case, as such queues are used solely for execution shaping. 842 | **/ 843 | - (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreConfiguredTargetQueue; 844 | - (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreviouslyConfiguredTargetQueue; 845 | 846 | /** 847 | * It's not thread-safe to access certain variables from outside the socket's internal queue. 848 | * 849 | * For example, the socket file descriptor. 850 | * File descriptors are simply integers which reference an index in the per-process file table. 851 | * However, when one requests a new file descriptor (by opening a file or socket), 852 | * the file descriptor returned is guaranteed to be the lowest numbered unused descriptor. 853 | * So if we're not careful, the following could be possible: 854 | * 855 | * - Thread A invokes a method which returns the socket's file descriptor. 856 | * - The socket is closed via the socket's internal queue on thread B. 857 | * - Thread C opens a file, and subsequently receives the file descriptor that was previously the socket's FD. 858 | * - Thread A is now accessing/altering the file instead of the socket. 859 | * 860 | * In addition to this, other variables are not actually objects, 861 | * and thus cannot be retained/released or even autoreleased. 862 | * An example is the sslContext, of type SSLContextRef, which is actually a malloc'd struct. 863 | * 864 | * Although there are internal variables that make it difficult to maintain thread-safety, 865 | * it is important to provide access to these variables 866 | * to ensure this class can be used in a wide array of environments. 867 | * This method helps to accomplish this by invoking the current block on the socket's internal queue. 868 | * The methods below can be invoked from within the block to access 869 | * those generally thread-unsafe internal variables in a thread-safe manner. 870 | * The given block will be invoked synchronously on the socket's internal queue. 871 | * 872 | * If you save references to any protected variables and use them outside the block, you do so at your own peril. 873 | **/ 874 | - (void)performBlock:(dispatch_block_t)block; 875 | 876 | /** 877 | * These methods are only available from within the context of a performBlock: invocation. 878 | * See the documentation for the performBlock: method above. 879 | * 880 | * Provides access to the socket's file descriptor(s). 881 | * If the socket isn't connected, or explicity bound to a particular interface, 882 | * it might actually have multiple internal socket file descriptors - one for IPv4 and one for IPv6. 883 | **/ 884 | - (int)socketFD; 885 | - (int)socket4FD; 886 | - (int)socket6FD; 887 | 888 | #if TARGET_OS_IPHONE 889 | 890 | /** 891 | * These methods are only available from within the context of a performBlock: invocation. 892 | * See the documentation for the performBlock: method above. 893 | * 894 | * Returns (creating if necessary) a CFReadStream/CFWriteStream for the internal socket. 895 | * 896 | * Generally GCDAsyncUdpSocket doesn't use CFStream. (It uses the faster GCD API's.) 897 | * However, if you need one for any reason, 898 | * these methods are a convenient way to get access to a safe instance of one. 899 | **/ 900 | - (CFReadStreamRef)readStream; 901 | - (CFWriteStreamRef)writeStream; 902 | 903 | /** 904 | * This method is only available from within the context of a performBlock: invocation. 905 | * See the documentation for the performBlock: method above. 906 | * 907 | * Configures the socket to allow it to operate when the iOS application has been backgrounded. 908 | * In other words, this method creates a read & write stream, and invokes: 909 | * 910 | * CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); 911 | * CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); 912 | * 913 | * Returns YES if successful, NO otherwise. 914 | * 915 | * Example usage: 916 | * 917 | * [asyncUdpSocket performBlock:^{ 918 | * [asyncUdpSocket enableBackgroundingOnSocket]; 919 | * }]; 920 | * 921 | * 922 | * NOTE : Apple doesn't currently support backgrounding UDP sockets. (Only TCP for now). 923 | **/ 924 | //- (BOOL)enableBackgroundingOnSockets; 925 | 926 | #endif 927 | 928 | #pragma mark Utilities 929 | 930 | /** 931 | * Extracting host/port/family information from raw address data. 932 | **/ 933 | 934 | + (NSString *)hostFromAddress:(NSData *)address; 935 | + (uint16_t)portFromAddress:(NSData *)address; 936 | + (int)familyFromAddress:(NSData *)address; 937 | 938 | + (BOOL)isIPv4Address:(NSData *)address; 939 | + (BOOL)isIPv6Address:(NSData *)address; 940 | 941 | + (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr fromAddress:(NSData *)address; 942 | + (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr family:(int *)afPtr fromAddress:(NSData *)address; 943 | 944 | @end 945 | 946 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 947 | #pragma mark - 948 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 949 | 950 | @protocol GCDAsyncUdpSocketDelegate 951 | @optional 952 | 953 | /** 954 | * By design, UDP is a connectionless protocol, and connecting is not needed. 955 | * However, you may optionally choose to connect to a particular host for reasons 956 | * outlined in the documentation for the various connect methods listed above. 957 | * 958 | * This method is called if one of the connect methods are invoked, and the connection is successful. 959 | **/ 960 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address; 961 | 962 | /** 963 | * By design, UDP is a connectionless protocol, and connecting is not needed. 964 | * However, you may optionally choose to connect to a particular host for reasons 965 | * outlined in the documentation for the various connect methods listed above. 966 | * 967 | * This method is called if one of the connect methods are invoked, and the connection fails. 968 | * This may happen, for example, if a domain name is given for the host and the domain name is unable to be resolved. 969 | **/ 970 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError *)error; 971 | 972 | /** 973 | * Called when the datagram with the given tag has been sent. 974 | **/ 975 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag; 976 | 977 | /** 978 | * Called if an error occurs while trying to send a datagram. 979 | * This could be due to a timeout, or something more serious such as the data being too large to fit in a sigle packet. 980 | **/ 981 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error; 982 | 983 | /** 984 | * Called when the socket has received the requested datagram. 985 | **/ 986 | - (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data 987 | fromAddress:(NSData *)address 988 | withFilterContext:(id)filterContext; 989 | 990 | /** 991 | * Called when the socket is closed. 992 | **/ 993 | - (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError *)error; 994 | 995 | @end 996 | 997 | -------------------------------------------------------------------------------- /AnyBar/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "size" : "128x128", 25 | "idiom" : "mac", 26 | "filename" : "icon_128x128.png", 27 | "scale" : "1x" 28 | }, 29 | { 30 | "size" : "128x128", 31 | "idiom" : "mac", 32 | "filename" : "icon_128x128@2x.png", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "idiom" : "mac", 37 | "size" : "256x256", 38 | "scale" : "1x" 39 | }, 40 | { 41 | "idiom" : "mac", 42 | "size" : "256x256", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "512x512", 47 | "idiom" : "mac", 48 | "filename" : "icon_512x512.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon_512x512@2x.png", 55 | "scale" : "2x" 56 | } 57 | ], 58 | "info" : { 59 | "version" : 1, 60 | "author" : "xcode" 61 | } 62 | } -------------------------------------------------------------------------------- /AnyBar/Images.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Images.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /AnyBar/Images.xcassets/AppIcon.appiconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Images.xcassets/AppIcon.appiconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /AnyBar/Images.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Images.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /AnyBar/Images.xcassets/AppIcon.appiconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Images.xcassets/AppIcon.appiconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /AnyBar/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | LSUIElement 28 | 29 | NSAppleScriptEnabled 30 | YES 31 | NSHumanReadableCopyright 32 | Copyright © 2020 Nikita Prokopov. All rights reserved. 33 | NSMainNibFile 34 | MainMenu 35 | NSPrincipalClass 36 | AnyBarApp 37 | OSAScriptingDefinition 38 | AnyBar 39 | 40 | 41 | -------------------------------------------------------------------------------- /AnyBar/Resources/black@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Resources/black@2x.png -------------------------------------------------------------------------------- /AnyBar/Resources/blue@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Resources/blue@2x.png -------------------------------------------------------------------------------- /AnyBar/Resources/cyan@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Resources/cyan@2x.png -------------------------------------------------------------------------------- /AnyBar/Resources/exclamation@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Resources/exclamation@2x.png -------------------------------------------------------------------------------- /AnyBar/Resources/filled@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Resources/filled@2x.png -------------------------------------------------------------------------------- /AnyBar/Resources/green@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Resources/green@2x.png -------------------------------------------------------------------------------- /AnyBar/Resources/hollow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Resources/hollow@2x.png -------------------------------------------------------------------------------- /AnyBar/Resources/orange@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Resources/orange@2x.png -------------------------------------------------------------------------------- /AnyBar/Resources/purple@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Resources/purple@2x.png -------------------------------------------------------------------------------- /AnyBar/Resources/question@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Resources/question@2x.png -------------------------------------------------------------------------------- /AnyBar/Resources/red@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Resources/red@2x.png -------------------------------------------------------------------------------- /AnyBar/Resources/white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Resources/white@2x.png -------------------------------------------------------------------------------- /AnyBar/Resources/yellow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/AnyBar/Resources/yellow@2x.png -------------------------------------------------------------------------------- /AnyBar/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // AnyBar 4 | // 5 | // Created by Nikita Prokopov on 14/02/15. 6 | // Copyright (c) 2015 Nikita Prokopov. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) { 12 | return NSApplicationMain(argc, argv); 13 | } 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.2.3 / Feb 18, 2021 2 | 3 | - Added `filled` and `hollow`, removed alternative versions of `black` and `white` #84 4 | 5 | ### 0.2.2 / Nov 3, 2020 6 | 7 | - Specify starting color via `ANYBAR_INIT` #80 #81 thx @Gira-X 8 | 9 | ### 0.2.1 / Aug 28, 2020 10 | 11 | - Correct version metadata 12 | 13 | ### 0.2.0 / Aug 26, 2020 14 | 15 | - Specify app tooltip via `ANYBAR_TITLE` #59 #64 thx @mynameismiek @andrewsjg 16 | 17 | ### 0.1.4 18 | 19 | - Bigger dots 20 | - Render “black” on dark menubar as empty circle and “white” on dark as filled circle (#55) 21 | - Compiled for OS X 10.11 22 | 23 | ### 0.1.3 24 | 25 | - AppleScript support (PR #8, thx [Oleg Kertanov](https://github.com/okertanov)) 26 | 27 | ### 0.1.2 28 | 29 | - Dark mode support. In dark mode AnyBar will first check for `_alt@2x.png` or `_alt.png` image first, then falls back to `.png` 30 | - Support for Mavericks actually works 31 | 32 | ### 0.1.1 33 | 34 | - Support for Mavericks (PR #2, thx [Oleg Kertanov](https://github.com/okertanov)) 35 | - Support for custom images via ~/.AnyBar (PR #1, thx [Paul Boschmann](https://github.com/pboschmann)) 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' 19 | from a Contributor if it was added to the Program by such Contributor 20 | itself or anyone acting on such Contributor's behalf. Contributions do not 21 | include additions to the Program which: (i) are separate modules of 22 | software distributed in conjunction with the Program under their own 23 | license agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this 32 | Agreement. 33 | 34 | "Recipient" means anyone who receives the Program under this Agreement, 35 | including all Contributors. 36 | 37 | 2. GRANT OF RIGHTS 38 | a) Subject to the terms of this Agreement, each Contributor hereby grants 39 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 40 | reproduce, prepare derivative works of, publicly display, publicly 41 | perform, distribute and sublicense the Contribution of such Contributor, 42 | if any, and such derivative works, in source code and object code form. 43 | b) Subject to the terms of this Agreement, each Contributor hereby grants 44 | Recipient a non-exclusive, worldwide, royalty-free patent license under 45 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 46 | transfer the Contribution of such Contributor, if any, in source code and 47 | object code form. This patent license shall apply to the combination of 48 | the Contribution and the Program if, at the time the Contribution is 49 | added by the Contributor, such addition of the Contribution causes such 50 | combination to be covered by the Licensed Patents. The patent license 51 | shall not apply to any other combinations which include the Contribution. 52 | No hardware per se is licensed hereunder. 53 | c) Recipient understands that although each Contributor grants the licenses 54 | to its Contributions set forth herein, no assurances are provided by any 55 | Contributor that the Program does not infringe the patent or other 56 | intellectual property rights of any other entity. Each Contributor 57 | disclaims any liability to Recipient for claims brought by any other 58 | entity based on infringement of intellectual property rights or 59 | otherwise. As a condition to exercising the rights and licenses granted 60 | hereunder, each Recipient hereby assumes sole responsibility to secure 61 | any other intellectual property rights needed, if any. For example, if a 62 | third party patent license is required to allow Recipient to distribute 63 | the Program, it is Recipient's responsibility to acquire that license 64 | before distributing the Program. 65 | d) Each Contributor represents that to its knowledge it has sufficient 66 | copyright rights in its Contribution, if any, to grant the copyright 67 | license set forth in this Agreement. 68 | 69 | 3. REQUIREMENTS 70 | 71 | A Contributor may choose to distribute the Program in object code form under 72 | its own license agreement, provided that: 73 | 74 | a) it complies with the terms and conditions of this Agreement; and 75 | b) its license agreement: 76 | i) effectively disclaims on behalf of all Contributors all warranties 77 | and conditions, express and implied, including warranties or 78 | conditions of title and non-infringement, and implied warranties or 79 | conditions of merchantability and fitness for a particular purpose; 80 | ii) effectively excludes on behalf of all Contributors all liability for 81 | damages, including direct, indirect, special, incidental and 82 | consequential damages, such as lost profits; 83 | iii) states that any provisions which differ from this Agreement are 84 | offered by that Contributor alone and not by any other party; and 85 | iv) states that source code for the Program is available from such 86 | Contributor, and informs licensees how to obtain it in a reasonable 87 | manner on or through a medium customarily used for software exchange. 88 | 89 | When the Program is made available in source code form: 90 | 91 | a) it must be made available under this Agreement; and 92 | b) a copy of this Agreement must be included with each copy of the Program. 93 | Contributors may not remove or alter any copyright notices contained 94 | within the Program. 95 | 96 | Each Contributor must identify itself as the originator of its Contribution, 97 | if 98 | any, in a manner that reasonably allows subsequent Recipients to identify the 99 | originator of the Contribution. 100 | 101 | 4. COMMERCIAL DISTRIBUTION 102 | 103 | Commercial distributors of software may accept certain responsibilities with 104 | respect to end users, business partners and the like. While this license is 105 | intended to facilitate the commercial use of the Program, the Contributor who 106 | includes the Program in a commercial product offering should do so in a manner 107 | which does not create potential liability for other Contributors. Therefore, 108 | if a Contributor includes the Program in a commercial product offering, such 109 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 110 | every other Contributor ("Indemnified Contributor") against any losses, 111 | damages and costs (collectively "Losses") arising from claims, lawsuits and 112 | other legal actions brought by a third party against the Indemnified 113 | Contributor to the extent caused by the acts or omissions of such Commercial 114 | Contributor in connection with its distribution of the Program in a commercial 115 | product offering. The obligations in this section do not apply to any claims 116 | or Losses relating to any actual or alleged intellectual property 117 | infringement. In order to qualify, an Indemnified Contributor must: 118 | a) promptly notify the Commercial Contributor in writing of such claim, and 119 | b) allow the Commercial Contributor to control, and cooperate with the 120 | Commercial Contributor in, the defense and any related settlement 121 | negotiations. The Indemnified Contributor may participate in any such claim at 122 | its own expense. 123 | 124 | For example, a Contributor might include the Program in a commercial product 125 | offering, Product X. That Contributor is then a Commercial Contributor. If 126 | that Commercial Contributor then makes performance claims, or offers 127 | warranties related to Product X, those performance claims and warranties are 128 | such Commercial Contributor's responsibility alone. Under this section, the 129 | Commercial Contributor would have to defend claims against the other 130 | Contributors related to those performance claims and warranties, and if a 131 | court requires any other Contributor to pay any damages as a result, the 132 | Commercial Contributor must pay those damages. 133 | 134 | 5. NO WARRANTY 135 | 136 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 137 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 138 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 140 | Recipient is solely responsible for determining the appropriateness of using 141 | and distributing the Program and assumes all risks associated with its 142 | exercise of rights under this Agreement , including but not limited to the 143 | risks and costs of program errors, compliance with applicable laws, damage to 144 | or loss of data, programs or equipment, and unavailability or interruption of 145 | operations. 146 | 147 | 6. DISCLAIMER OF LIABILITY 148 | 149 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 150 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 151 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 152 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 153 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 154 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 155 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 156 | OF SUCH DAMAGES. 157 | 158 | 7. GENERAL 159 | 160 | If any provision of this Agreement is invalid or unenforceable under 161 | applicable law, it shall not affect the validity or enforceability of the 162 | remainder of the terms of this Agreement, and without further action by the 163 | parties hereto, such provision shall be reformed to the minimum extent 164 | necessary to make such provision valid and enforceable. 165 | 166 | If Recipient institutes patent litigation against any entity (including a 167 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 168 | (excluding combinations of the Program with other software or hardware) 169 | infringes such Recipient's patent(s), then such Recipient's rights granted 170 | under Section 2(b) shall terminate as of the date such litigation is filed. 171 | 172 | All Recipient's rights under this Agreement shall terminate if it fails to 173 | comply with any of the material terms or conditions of this Agreement and does 174 | not cure such failure in a reasonable period of time after becoming aware of 175 | such noncompliance. If all Recipient's rights under this Agreement terminate, 176 | Recipient agrees to cease use and distribution of the Program as soon as 177 | reasonably practicable. However, Recipient's obligations under this Agreement 178 | and any licenses granted by Recipient relating to the Program shall continue 179 | and survive. 180 | 181 | Everyone is permitted to copy and distribute copies of this Agreement, but in 182 | order to avoid inconsistency the Agreement is copyrighted and may only be 183 | modified in the following manner. The Agreement Steward reserves the right to 184 | publish new versions (including revisions) of this Agreement from time to 185 | time. No one other than the Agreement Steward has the right to modify this 186 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 187 | Eclipse Foundation may assign the responsibility to serve as the Agreement 188 | Steward to a suitable separate entity. Each new version of the Agreement will 189 | be given a distinguishing version number. The Program (including 190 | Contributions) may always be distributed subject to the version of the 191 | Agreement under which it was received. In addition, after a new version of the 192 | Agreement is published, Contributor may elect to distribute the Program 193 | (including its Contributions) under the new version. Except as expressly 194 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 195 | licenses to the intellectual property of any Contributor under this Agreement, 196 | whether expressly, by implication, estoppel or otherwise. All rights in the 197 | Program not expressly granted under this Agreement are reserved. 198 | 199 | This Agreement is governed by the laws of the State of New York and the 200 | intellectual property laws of the United States of America. No party to this 201 | Agreement will bring a legal action under this Agreement more than one year 202 | after the cause of action arose. Each party waives its rights to a jury trial in 203 | any resulting litigation. 204 | 205 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AnyBar: OS X menubar status indicator 2 | 3 | AnyBar is a small indicator for your menubar that does one simple thing: it displays a colored dot. What the dot means and when to change it is up to you. 4 | 5 | 6 | 7 | ## Download 8 | 9 | Version 0.2.3: 10 | 11 | 12 | 13 | Or using [Homebrew Cask](https://github.com/Homebrew/homebrew-cask): 14 | 15 | brew install --cask anybar 16 | 17 | ## Support us 18 | 19 | 20 | 21 | ## Usage 22 | 23 | AnyBar is controlled via a UDP port (1738 by default). Before any commands can be sent, AnyBar.app must be launched: 24 | 25 | ```sh 26 | open -a AnyBar 27 | ``` 28 | 29 | Once launched, you may send it a message to change the style of the dot: 30 | 31 | ```sh 32 | echo -n "black" | nc -4u -w0 localhost 1738 33 | ``` 34 | 35 | If you use bash, you might prefer this instead: 36 | 37 | ```sh 38 | bash -c 'echo -n "black" > /dev/udp/localhost/1738' 39 | ``` 40 | 41 | The following default commands change the style of the dot: 42 | 43 | | Command | Preview | 44 | |---------------|---------------------------------------------------------------------| 45 | | `white` | | 46 | | `red` | | 47 | | `orange` | | 48 | | `yellow` | | 49 | | `green` | | 50 | | `cyan` | | 51 | | `blue` | | 52 | | `purple` | | 53 | | `black` | | 54 | | `question` | | 55 | | `exclamation` | | 56 | | `filled` | | 57 | | `hollow` | | 58 | 59 | `black` and `white` always has black or white fill. On Big Sur, where text color of menubar might change depending on the wallpaper, you might want to use `filled` and `hollow` instead. They are inverted when menubar changes its appearance. 60 | 61 | To quit, send `quit`. 62 | 63 | ## Alternative clients 64 | 65 | Bash alias: 66 | 67 | ```sh 68 | $ function anybar { echo -n $1 | nc -4u -w0 localhost ${2:-1738}; } 69 | 70 | $ anybar red 71 | $ anybar green 1739 72 | ``` 73 | 74 | Or with /dev/udp: 75 | 76 | ```sh 77 | function anybar { echo -n $1 > /dev/udp/localhost/${2:-1738}; } 78 | ``` 79 | 80 | Zsh with completion: 81 | 82 | - [wookayin/anybar-zsh](https://github.com/wookayin/anybar-zsh) 83 | 84 | Fish shell with completion: 85 | 86 | - [matchai/anybar-fish](https://github.com/matchai/anybar-fish) 87 | 88 | Go: 89 | 90 | - [justincampbell/anybar](https://github.com/justincampbell/anybar) 91 | - [johntdyer/anybar-go](https://github.com/johntdyer/anybar-go) 92 | 93 | Node: 94 | 95 | - [rumpl/nanybar](https://github.com/rumpl/nanybar) 96 | - [sindresorhus/anybar](https://github.com/sindresorhus/anybar) 97 | - [snippet by skibz](https://github.com/tonsky/AnyBar/issues/11) 98 | 99 | Deno: 100 | 101 | - [pumpncode/anybar](https://github.com/pumpncode/anybar) 102 | 103 | PHP: 104 | 105 | - [2bj/Phanybar](https://github.com/2bj/Phanybar) 106 | 107 | Java: 108 | 109 | - [cs475x/AnyBar4j](https://github.com/cs475x/AnyBar4j) 110 | 111 | Python: 112 | 113 | - [philipbl/pyanybar](https://github.com/philipbl/pyAnyBar) 114 | 115 | Ruby: 116 | 117 | - [davydovanton/AnyBar_rb](https://github.com/davydovanton/AnyBar_rb) 118 | 119 | Rust: 120 | 121 | - [urschrei/rust_anybar](https://github.com/urschrei/rust_anybar) 122 | - [Feliix42/anybar-rs](https://github.com/Feliix42/anybar-rs) 123 | 124 | Nim: 125 | 126 | - [rgv151/anybar.nim](https://github.com/rgv151/anybar.nim) 127 | 128 | Erlang: 129 | 130 | - [kureikain/ebar](https://github.com/kureikain/ebar) 131 | 132 | C: 133 | 134 | - [onderweg/anybar-cli](https://github.com/onderweg/anybar-cli) 135 | 136 | C#: 137 | 138 | - [jenyayel/anybar-client](https://github.com/jenyayel/anybar-client) 139 | 140 | Crystal: 141 | - [davydovanton/AnyBar_cr](https://github.com/davydovanton/AnyBar_cr) 142 | 143 | Emacs: 144 | 145 | - [rmuslimov/anybar.el](https://gist.github.com/rmuslimov/2d74cacd5e0ae827663e) 146 | - [tie-rack/anybar-el](https://github.com/tie-rack/anybar-el) (Also on [Melpa](https://melpa.org/#/anybar)) 147 | 148 | AppleScript: 149 | 150 | ```applescript 151 | tell application "AnyBar" to set image name to "blue" 152 | 153 | tell application "AnyBar" to set current to get image name as Unicode text 154 | display notification current 155 | ``` 156 | 157 | Alfred: 158 | 159 | - [https://github.com/raguay/MyAlfred](https://github.com/raguay/MyAlfred/blob/master/Alfred%204/AnyBar%20Workflow.alfredworkflow) 160 | 161 | ## Integrations 162 | 163 | - Webpack build status plugin [roman01la/anybar-webpack](https://github.com/roman01la/anybar-webpack) 164 | - boot-clj task [tonsky/boot-anybar](https://github.com/tonsky/boot-anybar) 165 | - Idea plugin [denofevil/AnyBarIdea](https://github.com/denofevil/AnyBarIdea) 166 | - Anybar-based CLI journal [Andrew565/anybar-icon-journal](https://github.com/Andrew565/anybar-icon-journal) 167 | - Command monitoring [rvirani1/with_anybar](https://github.com/rvirani1/with_anybar) 168 | - Monitor commands automatically, across several iterm tabs [stacycurl/anybar_bash](https://github.com/stacycurl/anybar-bash) 169 | - Extension for ipython/jupyter/ipython notebook [ermakovpetr/ipython-anybar](https://github.com/ermakovpetr/ipython-anybar) 170 | 171 | ## Running multiple instances 172 | 173 | You can run several instances of AnyBar as long as they listen on different ports. Use the `ANYBAR_PORT` environment variable to change the port and `open -na` to run several instances: 174 | 175 | ```sh 176 | ANYBAR_PORT=1738 open -na AnyBar 177 | ANYBAR_PORT=1739 open -na AnyBar 178 | ANYBAR_PORT=1740 open -na AnyBar 179 | ``` 180 | 181 | ## Environment variables to specify a title and the initial color of the dot 182 | 183 | A title can be set to distinguish dots in the menubar: 184 | 185 | ```sh 186 | ANYBAR_PORT=1738 ANYBAR_TITLE=First open -na AnyBar 187 | ANYBAR_PORT=1739 ANYBAR_TITLE=Second open -na AnyBar 188 | ANYBAR_PORT=1740 ANYBAR_TITLE=Third open -na AnyBar 189 | ``` 190 | 191 | And the initial color of the dot can also be set: 192 | 193 | ```sh 194 | ANYBAR_INIT=blue open -na AnyBar 195 | ``` 196 | 197 | ## Custom images 198 | 199 | AnyBar can detect and use local custom images stored in the `~/.AnyBar` directory. For example, if you have a `~/.AnyBar/square@2x.png` image, send `square` to port 1738 and it will be displayed. Images should be 19×19 pixels for standard resolution, and 38x38 pixels for retina (@2x). 200 | 201 | ## Ports 202 | 203 | - Ubuntu Unity [limpbrains/somebar](https://github.com/limpbrains/somebar) 204 | - i3wm with i3pystatus [enkore/i3pystatus](https://github.com/enkore/i3pystatus) 205 | - Windows 10 [PavelStefanov/NoteBar](https://github.com/PavelStefanov/NoteBar) 206 | - Emacs [plexus/.../emybar.el](https://github.com/plexus/plexmacs/blob/master/emybar/emybar.el) 207 | 208 | ## License 209 | 210 | Copyright © 2015 Nikita Prokopov 211 | 212 | Licensed under Eclipse Public License (see [LICENSE](LICENSE)). 213 | -------------------------------------------------------------------------------- /Tests/Makefile: -------------------------------------------------------------------------------- 1 | SHELL:=bash 2 | SLEEPT:=3s 3 | PORTS:=1738 1739 1740 4 | 5 | all: tests 6 | 7 | tests: anybar 8 | @echo "Starting..." && \ 9 | echo -n " WHITE " && \ 10 | for port in $(PORTS) ; do ANYBAR_PORT=$$port open -n ../build/Debug/AnyBar.app ; done && \ 11 | sleep $(SLEEPT) && \ 12 | echo -n " ORANGE " && \ 13 | for port in $(PORTS) ; do echo -n "orange" | nc -4u -w0 localhost $$port ; done && \ 14 | sleep $(SLEEPT) && \ 15 | echo -n " RGB " && \ 16 | echo -n "red" | nc -4u -w0 localhost 1738 && \ 17 | echo -n "green" | nc -4u -w0 localhost 1739 && \ 18 | echo -n "blue" | nc -4u -w0 localhost 1740 && \ 19 | sleep $(SLEEPT) && \ 20 | echo -n " BLACK " && \ 21 | for port in $(PORTS) ; do echo -n "black" | nc -4u -w0 localhost $$port ; done && \ 22 | sleep $(SLEEPT) && \ 23 | echo -n " WHITE " && \ 24 | for port in $(PORTS) ; do echo -n "white" | nc -4u -w0 localhost $$port ; done && \ 25 | sleep $(SLEEPT) && \ 26 | echo && \ 27 | echo "Stopping..." && \ 28 | for port in $(PORTS) ; do echo -n "quit" | nc -4u -w0 localhost $$port ; done && \ 29 | sleep $(SLEEPT) && \ 30 | echo "Done." 31 | 32 | anybar: ../AnyBar.xcodeproj 33 | @xcodebuild -configuration Debug -target AnyBar -project $< build 34 | 35 | clean: ../AnyBar.xcodeproj 36 | -@xcodebuild -configuration Debug -target AnyBar -project $< clean 2>&1>/dev/null 37 | -@rm -rf ../build 38 | 39 | .PHONY: all tests anybar clean 40 | 41 | .SILENT: clean 42 | 43 | -------------------------------------------------------------------------------- /Tests/control-anybar.applescript: -------------------------------------------------------------------------------- 1 | tell application "AnyBar" to set image name to "blue" 2 | tell application "AnyBar" to set current to get image name as Unicode text 3 | display notification current -------------------------------------------------------------------------------- /Tests/osa-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env osascript 2 | 3 | tell application "AnyBar.app" 4 | launch 5 | activate 6 | end tell 7 | 8 | delay 3 9 | 10 | tell application "AnyBar.app" 11 | set image name to "green" 12 | display notification image name as Unicode text 13 | end tell 14 | 15 | delay 3 16 | 17 | tell application "AnyBar.app" 18 | quit 19 | end tell 20 | 21 | -------------------------------------------------------------------------------- /Tests/test-osa-script.applescript: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/Tests/test-osa-script.applescript -------------------------------------------------------------------------------- /anybar_patreon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/anybar_patreon.png -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonsky/AnyBar/4e1b32d226eb045cc142a8cdb43acceb79fa4aef/screenshot.png --------------------------------------------------------------------------------