├── .buildkite
├── pipeline.yml
└── publish-pod.sh
├── .bundle
└── config
├── .github
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .rubocop.yml
├── .ruby-version
├── AppledocSettings.plist
├── CHANGELOG.md
├── Gemfile
├── Gemfile.lock
├── LICENSE.md
├── Package.swift
├── README.md
├── Support
├── Info-Tests.plist
└── Info.plist
├── WPXMLRPC
├── WPBase64Utils.h
├── WPBase64Utils.m
├── WPStringUtils.h
├── WPStringUtils.m
├── WPXMLRPC.h
├── WPXMLRPCDataCleaner.h
├── WPXMLRPCDataCleaner.m
├── WPXMLRPCDecoder.h
├── WPXMLRPCDecoder.m
├── WPXMLRPCDecoderDelegate.h
├── WPXMLRPCDecoderDelegate.m
├── WPXMLRPCEncoder.h
├── WPXMLRPCEncoder.m
└── include
│ ├── WPXMLRPC.h
│ ├── WPXMLRPCDecoder.h
│ └── WPXMLRPCEncoder.h
├── WPXMLRPCTest
├── Test Data
│ ├── AlternativeDateFormatsTestCase.xml
│ ├── DefaultTypeTestCase.xml
│ ├── EmptyBooleanTestCase.xml
│ ├── EmptyDataTestCase.xml
│ ├── EmptyDoubleTestCase.xml
│ ├── EmptyIntegerTestCase.xml
│ ├── EmptyStringTestCase.xml
│ ├── EscapingTestCase.xml
│ ├── IncompleteXmlTest.xml
│ ├── InvalidFault.xml
│ ├── NoXmlResponseTestCase.xml
│ ├── OverflowInteger32TestCase.xml
│ ├── RequestTestCase.xml
│ ├── SimpleArrayTestCase.xml
│ ├── SimpleStructTestCase.xml
│ ├── TestCases.plist
│ ├── TestImage.base64
│ ├── TestImage.bin
│ ├── entities.xml
│ └── entitiesDecoded.xml
└── Tests
│ ├── Helper.h
│ ├── Helper.m
│ ├── WPBase64UtilsTest.m
│ ├── WPXMLRPCDataCleanerTest.m
│ ├── WPXMLRPCDecoderTest.m
│ ├── WPXMLRPCEncoderTest.m
│ └── WPXMLRPCEntitiesTest.m
├── fastlane
└── Fastfile
└── wpxmlrpc.podspec
/.buildkite/pipeline.yml:
--------------------------------------------------------------------------------
1 | # Nodes with values to reuse in the pipeline.
2 | common_params:
3 | plugins: &common_plugins
4 | - automattic/a8c-ci-toolkit#2.17.0
5 | env: &common_env
6 | IMAGE_ID: xcode-15.2
7 |
8 | # This is the default pipeline – it will build and test the pod
9 | steps:
10 | ########################
11 | # Validate Swift Package
12 | ########################
13 | - label: "🔬 Validate Swift Package"
14 | key: "test"
15 | command: "validate_swift_package"
16 | env: *common_env
17 | plugins: *common_plugins
18 |
19 | #################
20 | # Validate Podspec
21 | #################
22 | - label: "🔬 Validate Podspec"
23 | key: "validate"
24 | command: "validate_podspec"
25 | env: *common_env
26 | plugins: *common_plugins
27 |
28 | #################
29 | # Lint
30 | #################
31 | - label: "🧹 Lint"
32 | key: "lint"
33 | command: "lint_pod"
34 | env: *common_env
35 | plugins: *common_plugins
36 |
37 | #################
38 | # Publish the Podspec (if we're building a tag)
39 | #################
40 | - label: "⬆️ Publish Podspec"
41 | key: "publish"
42 | command: .buildkite/publish-pod.sh
43 | env: *common_env
44 | plugins: *common_plugins
45 | depends_on:
46 | - "test"
47 | - "validate"
48 | - "lint"
49 | if: build.tag != null
50 | agents:
51 | queue: "mac"
52 |
--------------------------------------------------------------------------------
/.buildkite/publish-pod.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -eu
2 |
3 | PODSPEC_PATH="wpxmlrpc.podspec"
4 | SLACK_WEBHOOK=$PODS_SLACK_WEBHOOK
5 |
6 | echo "--- :rubygems: Setting up Gems"
7 | install_gems
8 |
9 | echo "--- :cocoapods: Publishing Pod to CocoaPods CDN"
10 | publish_pod $PODSPEC_PATH
11 |
12 | echo "--- :slack: Notifying Slack"
13 | slack_notify_pod_published $PODSPEC_PATH $SLACK_WEBHOOK
14 |
--------------------------------------------------------------------------------
/.bundle/config:
--------------------------------------------------------------------------------
1 | ---
2 | BUNDLE_PATH: "vendor/bundle"
3 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 | ---
3 |
4 | - [ ] I have considered if this change warrants release notes and have added them to the appropriate section in the `CHANGELOG.md` if necessary.
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 |
9 | ## Various settings
10 | .DS_Store
11 | *.pbxuser
12 | !default.pbxuser
13 | *.mode1v3
14 | !default.mode1v3
15 | *.mode2v3
16 | !default.mode2v3
17 | *.perspectivev3
18 | !default.perspectivev3
19 | xcuserdata
20 |
21 | ## Other
22 | *.xccheckout
23 | *.moved-aside
24 | *.xcuserstate
25 | *.xcscmblueprint
26 |
27 | ## Obj-C/Swift specific
28 | *.hmap
29 | *.ipa
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | .build/
40 |
41 | # CocoaPods
42 | #
43 | # We recommend against adding the Pods directory to your .gitignore. However
44 | # you should judge for yourself, the pros and cons are mentioned at:
45 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
46 | #
47 | WPXMLRPCTest/Pods/*
48 |
49 | # Carthage
50 | #
51 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
52 | # Carthage/Checkouts
53 |
54 | Carthage/Build
55 |
56 | # fastlane
57 | #
58 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
59 | # screenshots whenever they are needed.
60 | # For more information about the recommended setup visit:
61 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
62 |
63 | fastlane/report.xml
64 | fastlane/screenshots
65 | fastlane/test_output
66 | fastlane/README.md
67 |
68 | /vendor
69 |
70 | .swiftpm
71 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | # Opt in to new cops by default
2 | AllCops:
3 | NewCops: enable
4 |
5 | # Allow the Podspec filename to match the project
6 | Naming/FileName:
7 | Exclude:
8 | - 'wpxmlrpc.podspec'
9 |
10 | # Override the maximum line length
11 | Layout/LineLength:
12 | Max: 160
13 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.2.2
2 |
--------------------------------------------------------------------------------
/AppledocSettings.plist:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | --project-name
7 | WPXMLRPC
8 | --project-company
9 | WordPress
10 | --company-id
11 | org.wordpress
12 | --create-docset
13 |
14 | --install-docset
15 |
16 | --verbose
17 | 4
18 | --logformat
19 | 1
20 | --index-desc
21 | README.md
22 | --input
23 |
24 | WPXMLRPC/WPXMLRPCEncoder.h
25 | WPXMLRPC/WPXMLRPCDecoder.h
26 |
27 | --output
28 | docs
29 |
30 |
31 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | The format of this document is inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4 |
5 |
32 |
33 | ## Unreleased
34 |
35 | ### Breaking Changes
36 |
37 | - Remove tvOS support [#77]
38 |
39 | ### New Features
40 |
41 | _None._
42 |
43 | ### Bug Fixes
44 |
45 | _None._
46 |
47 | ### Internal Changes
48 |
49 | _None._
50 |
51 | ## [0.10.0](https://github.com/wordpress-mobile/wpxmlrpc/releases/tag/0.10.0)
52 |
53 | ### New Features
54 |
55 | - Add Swift Package Manager support [#66]
56 |
57 | ### Internal Changes
58 |
59 | - Added this changelog file [#67]
60 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source 'https://rubygems.org'
4 |
5 | gem 'cocoapods', '~> 1.10'
6 | gem 'cocoapods-check', '~> 1.1'
7 | gem 'fastlane', '~> 2.189'
8 | gem 'rubocop', '~> 1.18'
9 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.6)
5 | rexml
6 | activesupport (7.1.3)
7 | base64
8 | bigdecimal
9 | concurrent-ruby (~> 1.0, >= 1.0.2)
10 | connection_pool (>= 2.2.5)
11 | drb
12 | i18n (>= 1.6, < 2)
13 | minitest (>= 5.1)
14 | mutex_m
15 | tzinfo (~> 2.0)
16 | addressable (2.8.6)
17 | public_suffix (>= 2.0.2, < 6.0)
18 | algoliasearch (1.27.5)
19 | httpclient (~> 2.8, >= 2.8.3)
20 | json (>= 1.5.1)
21 | artifactory (3.0.15)
22 | ast (2.4.2)
23 | atomos (0.1.3)
24 | aws-eventstream (1.3.0)
25 | aws-partitions (1.884.0)
26 | aws-sdk-core (3.191.0)
27 | aws-eventstream (~> 1, >= 1.3.0)
28 | aws-partitions (~> 1, >= 1.651.0)
29 | aws-sigv4 (~> 1.8)
30 | jmespath (~> 1, >= 1.6.1)
31 | aws-sdk-kms (1.77.0)
32 | aws-sdk-core (~> 3, >= 3.191.0)
33 | aws-sigv4 (~> 1.1)
34 | aws-sdk-s3 (1.143.0)
35 | aws-sdk-core (~> 3, >= 3.191.0)
36 | aws-sdk-kms (~> 1)
37 | aws-sigv4 (~> 1.8)
38 | aws-sigv4 (1.8.0)
39 | aws-eventstream (~> 1, >= 1.0.2)
40 | babosa (1.0.4)
41 | base64 (0.2.0)
42 | bigdecimal (3.1.6)
43 | claide (1.1.0)
44 | cocoapods (1.15.0)
45 | addressable (~> 2.8)
46 | claide (>= 1.0.2, < 2.0)
47 | cocoapods-core (= 1.15.0)
48 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
49 | cocoapods-downloader (>= 2.1, < 3.0)
50 | cocoapods-plugins (>= 1.0.0, < 2.0)
51 | cocoapods-search (>= 1.0.0, < 2.0)
52 | cocoapods-trunk (>= 1.6.0, < 2.0)
53 | cocoapods-try (>= 1.1.0, < 2.0)
54 | colored2 (~> 3.1)
55 | escape (~> 0.0.4)
56 | fourflusher (>= 2.3.0, < 3.0)
57 | gh_inspector (~> 1.0)
58 | molinillo (~> 0.8.0)
59 | nap (~> 1.0)
60 | ruby-macho (>= 2.3.0, < 3.0)
61 | xcodeproj (>= 1.23.0, < 2.0)
62 | cocoapods-check (1.1.0)
63 | cocoapods (~> 1.0)
64 | cocoapods-core (1.15.0)
65 | activesupport (>= 5.0, < 8)
66 | addressable (~> 2.8)
67 | algoliasearch (~> 1.0)
68 | concurrent-ruby (~> 1.1)
69 | fuzzy_match (~> 2.0.4)
70 | nap (~> 1.0)
71 | netrc (~> 0.11)
72 | public_suffix (~> 4.0)
73 | typhoeus (~> 1.0)
74 | cocoapods-deintegrate (1.0.5)
75 | cocoapods-downloader (2.1)
76 | cocoapods-plugins (1.0.0)
77 | nap
78 | cocoapods-search (1.0.1)
79 | cocoapods-trunk (1.6.0)
80 | nap (>= 0.8, < 2.0)
81 | netrc (~> 0.11)
82 | cocoapods-try (1.2.0)
83 | colored (1.2)
84 | colored2 (3.1.2)
85 | commander (4.6.0)
86 | highline (~> 2.0.0)
87 | concurrent-ruby (1.2.3)
88 | connection_pool (2.4.1)
89 | declarative (0.0.20)
90 | digest-crc (0.6.5)
91 | rake (>= 12.0.0, < 14.0.0)
92 | domain_name (0.6.20240107)
93 | dotenv (2.8.1)
94 | drb (2.2.0)
95 | ruby2_keywords
96 | emoji_regex (3.2.3)
97 | escape (0.0.4)
98 | ethon (0.16.0)
99 | ffi (>= 1.15.0)
100 | excon (0.109.0)
101 | faraday (1.10.3)
102 | faraday-em_http (~> 1.0)
103 | faraday-em_synchrony (~> 1.0)
104 | faraday-excon (~> 1.1)
105 | faraday-httpclient (~> 1.0)
106 | faraday-multipart (~> 1.0)
107 | faraday-net_http (~> 1.0)
108 | faraday-net_http_persistent (~> 1.0)
109 | faraday-patron (~> 1.0)
110 | faraday-rack (~> 1.0)
111 | faraday-retry (~> 1.0)
112 | ruby2_keywords (>= 0.0.4)
113 | faraday-cookie_jar (0.0.7)
114 | faraday (>= 0.8.0)
115 | http-cookie (~> 1.0.0)
116 | faraday-em_http (1.0.0)
117 | faraday-em_synchrony (1.0.0)
118 | faraday-excon (1.1.0)
119 | faraday-httpclient (1.0.1)
120 | faraday-multipart (1.0.4)
121 | multipart-post (~> 2)
122 | faraday-net_http (1.0.1)
123 | faraday-net_http_persistent (1.2.0)
124 | faraday-patron (1.0.0)
125 | faraday-rack (1.0.0)
126 | faraday-retry (1.0.3)
127 | faraday_middleware (1.2.0)
128 | faraday (~> 1.0)
129 | fastimage (2.3.0)
130 | fastlane (2.219.0)
131 | CFPropertyList (>= 2.3, < 4.0.0)
132 | addressable (>= 2.8, < 3.0.0)
133 | artifactory (~> 3.0)
134 | aws-sdk-s3 (~> 1.0)
135 | babosa (>= 1.0.3, < 2.0.0)
136 | bundler (>= 1.12.0, < 3.0.0)
137 | colored
138 | commander (~> 4.6)
139 | dotenv (>= 2.1.1, < 3.0.0)
140 | emoji_regex (>= 0.1, < 4.0)
141 | excon (>= 0.71.0, < 1.0.0)
142 | faraday (~> 1.0)
143 | faraday-cookie_jar (~> 0.0.6)
144 | faraday_middleware (~> 1.0)
145 | fastimage (>= 2.1.0, < 3.0.0)
146 | gh_inspector (>= 1.1.2, < 2.0.0)
147 | google-apis-androidpublisher_v3 (~> 0.3)
148 | google-apis-playcustomapp_v1 (~> 0.1)
149 | google-cloud-env (>= 1.6.0, < 2.0.0)
150 | google-cloud-storage (~> 1.31)
151 | highline (~> 2.0)
152 | http-cookie (~> 1.0.5)
153 | json (< 3.0.0)
154 | jwt (>= 2.1.0, < 3)
155 | mini_magick (>= 4.9.4, < 5.0.0)
156 | multipart-post (>= 2.0.0, < 3.0.0)
157 | naturally (~> 2.2)
158 | optparse (>= 0.1.1)
159 | plist (>= 3.1.0, < 4.0.0)
160 | rubyzip (>= 2.0.0, < 3.0.0)
161 | security (= 0.1.3)
162 | simctl (~> 1.6.3)
163 | terminal-notifier (>= 2.0.0, < 3.0.0)
164 | terminal-table (~> 3)
165 | tty-screen (>= 0.6.3, < 1.0.0)
166 | tty-spinner (>= 0.8.0, < 1.0.0)
167 | word_wrap (~> 1.0.0)
168 | xcodeproj (>= 1.13.0, < 2.0.0)
169 | xcpretty (~> 0.3.0)
170 | xcpretty-travis-formatter (>= 0.0.3)
171 | ffi (1.16.3)
172 | fourflusher (2.3.1)
173 | fuzzy_match (2.0.4)
174 | gh_inspector (1.1.3)
175 | google-apis-androidpublisher_v3 (0.54.0)
176 | google-apis-core (>= 0.11.0, < 2.a)
177 | google-apis-core (0.11.3)
178 | addressable (~> 2.5, >= 2.5.1)
179 | googleauth (>= 0.16.2, < 2.a)
180 | httpclient (>= 2.8.1, < 3.a)
181 | mini_mime (~> 1.0)
182 | representable (~> 3.0)
183 | retriable (>= 2.0, < 4.a)
184 | rexml
185 | google-apis-iamcredentials_v1 (0.17.0)
186 | google-apis-core (>= 0.11.0, < 2.a)
187 | google-apis-playcustomapp_v1 (0.13.0)
188 | google-apis-core (>= 0.11.0, < 2.a)
189 | google-apis-storage_v1 (0.31.0)
190 | google-apis-core (>= 0.11.0, < 2.a)
191 | google-cloud-core (1.6.1)
192 | google-cloud-env (>= 1.0, < 3.a)
193 | google-cloud-errors (~> 1.0)
194 | google-cloud-env (1.6.0)
195 | faraday (>= 0.17.3, < 3.0)
196 | google-cloud-errors (1.3.1)
197 | google-cloud-storage (1.47.0)
198 | addressable (~> 2.8)
199 | digest-crc (~> 0.4)
200 | google-apis-iamcredentials_v1 (~> 0.1)
201 | google-apis-storage_v1 (~> 0.31.0)
202 | google-cloud-core (~> 1.6)
203 | googleauth (>= 0.16.2, < 2.a)
204 | mini_mime (~> 1.0)
205 | googleauth (1.8.1)
206 | faraday (>= 0.17.3, < 3.a)
207 | jwt (>= 1.4, < 3.0)
208 | multi_json (~> 1.11)
209 | os (>= 0.9, < 2.0)
210 | signet (>= 0.16, < 2.a)
211 | highline (2.0.3)
212 | http-cookie (1.0.5)
213 | domain_name (~> 0.5)
214 | httpclient (2.8.3)
215 | i18n (1.14.1)
216 | concurrent-ruby (~> 1.0)
217 | jmespath (1.6.2)
218 | json (2.7.1)
219 | jwt (2.7.1)
220 | language_server-protocol (3.17.0.3)
221 | mini_magick (4.12.0)
222 | mini_mime (1.1.5)
223 | minitest (5.21.2)
224 | molinillo (0.8.0)
225 | multi_json (1.15.0)
226 | multipart-post (2.3.0)
227 | mutex_m (0.2.0)
228 | nanaimo (0.3.0)
229 | nap (1.1.0)
230 | naturally (2.2.1)
231 | netrc (0.11.0)
232 | optparse (0.4.0)
233 | os (1.1.4)
234 | parallel (1.24.0)
235 | parser (3.3.0.5)
236 | ast (~> 2.4.1)
237 | racc
238 | plist (3.7.1)
239 | public_suffix (4.0.7)
240 | racc (1.7.3)
241 | rainbow (3.1.1)
242 | rake (13.1.0)
243 | regexp_parser (2.9.0)
244 | representable (3.2.0)
245 | declarative (< 0.1.0)
246 | trailblazer-option (>= 0.1.1, < 0.2.0)
247 | uber (< 0.2.0)
248 | retriable (3.1.2)
249 | rexml (3.2.6)
250 | rouge (2.0.7)
251 | rubocop (1.60.2)
252 | json (~> 2.3)
253 | language_server-protocol (>= 3.17.0)
254 | parallel (~> 1.10)
255 | parser (>= 3.3.0.2)
256 | rainbow (>= 2.2.2, < 4.0)
257 | regexp_parser (>= 1.8, < 3.0)
258 | rexml (>= 3.2.5, < 4.0)
259 | rubocop-ast (>= 1.30.0, < 2.0)
260 | ruby-progressbar (~> 1.7)
261 | unicode-display_width (>= 2.4.0, < 3.0)
262 | rubocop-ast (1.30.0)
263 | parser (>= 3.2.1.0)
264 | ruby-macho (2.5.1)
265 | ruby-progressbar (1.13.0)
266 | ruby2_keywords (0.0.5)
267 | rubyzip (2.3.2)
268 | security (0.1.3)
269 | signet (0.18.0)
270 | addressable (~> 2.8)
271 | faraday (>= 0.17.5, < 3.a)
272 | jwt (>= 1.5, < 3.0)
273 | multi_json (~> 1.10)
274 | simctl (1.6.10)
275 | CFPropertyList
276 | naturally
277 | terminal-notifier (2.0.0)
278 | terminal-table (3.0.2)
279 | unicode-display_width (>= 1.1.1, < 3)
280 | trailblazer-option (0.1.2)
281 | tty-cursor (0.7.1)
282 | tty-screen (0.8.2)
283 | tty-spinner (0.9.3)
284 | tty-cursor (~> 0.7)
285 | typhoeus (1.4.1)
286 | ethon (>= 0.9.0)
287 | tzinfo (2.0.6)
288 | concurrent-ruby (~> 1.0)
289 | uber (0.1.0)
290 | unicode-display_width (2.5.0)
291 | word_wrap (1.0.0)
292 | xcodeproj (1.24.0)
293 | CFPropertyList (>= 2.3.3, < 4.0)
294 | atomos (~> 0.1.3)
295 | claide (>= 1.0.2, < 2.0)
296 | colored2 (~> 3.1)
297 | nanaimo (~> 0.3.0)
298 | rexml (~> 3.2.4)
299 | xcpretty (0.3.0)
300 | rouge (~> 2.0.7)
301 | xcpretty-travis-formatter (1.0.1)
302 | xcpretty (~> 0.2, >= 0.0.7)
303 |
304 | PLATFORMS
305 | ruby
306 |
307 | DEPENDENCIES
308 | cocoapods (~> 1.10)
309 | cocoapods-check (~> 1.1)
310 | fastlane (~> 2.189)
311 | rubocop (~> 1.18)
312 |
313 | BUNDLED WITH
314 | 2.4.10
315 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # License
2 |
3 | ## WordPress XML-RPC library is distributed under the MIT License:
4 |
5 | Copyright (c) 2013 WordPress
6 | Copyright (c) 2012 Eric Czarny
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy of
9 | this software and associated documentation files (the "Software"), to deal in
10 | the Software without restriction, including without limitation the rights to
11 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 | of the Software, and to permit persons to whom the Software is furnished to do
13 | so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in all
16 | copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 | SOFTWARE.
25 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.5
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "wpxmlrpc",
7 | platforms: [.macOS(.v10_13), .iOS(.v11), .tvOS(.v11)],
8 | products: [
9 | .library(name: "wpxmlrpc", targets: ["wpxmlrpc"])
10 | ],
11 | dependencies: [],
12 | targets: [
13 | .target(
14 | name: "wpxmlrpc",
15 | path: "WPXMLRPC",
16 | linkerSettings: [.linkedLibrary("iconv")]
17 | ),
18 | .testTarget(
19 | name: "Tests",
20 | dependencies: [.target(name: "wpxmlrpc")],
21 | path: "WPXMLRPCTest",
22 | resources: [.process("Test Data")],
23 | cSettings: [.headerSearchPath("../WPXMLRPC")]
24 | )
25 | ]
26 | )
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WordPress XML-RPC Framework
2 |
3 | The WordPress XML-RPC library is a lightweight XML-RPC client for iOS
4 | and OS X.
5 |
6 | It's based on Eric Czarny's Cocoa XML-RPC Framework, but without all the
7 | networking code, and a few additions of our own.
8 |
9 | # Installation
10 |
11 | WordPress XML-RPC uses [CocoaPods](http://cocoapods.org/) for easy
12 | dependency management.
13 |
14 | Just add this to your Podfile and run `pod install`:
15 |
16 | pod 'wpxmlrpc'
17 |
18 | Another option, if you don't use CocoaPods, is to copy the `WPXMLRPC`
19 | folder to your project.
20 |
21 | # Usage
22 |
23 | WordPress XML-RPC only provides classes to encode and decode XML-RPC. You are free to use your favorite networking library.
24 |
25 | ## Building a XML-RPC request
26 |
27 | NSURL *URL = [NSURL URLWithString:@"http://example.com/xmlrpc"];
28 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
29 | [request setHTTPMethod:@"POST"];
30 |
31 | WPXMLRPCEncoder *encoder = [[WPXMLRPCEncoder alloc] initWithMethod:@"demo.addTwoNumbers" andParameters:@[@1, @2]];
32 | [request setHTTPBody:[encoder dataEncodedWithError:nil]];
33 |
34 | ## Building a XML-RPC request using streaming
35 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
36 | NSString *directory = [paths objectAtIndex:0];
37 | NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
38 | NSString *streamingCacheFilePath = [directory stringByAppendingPathComponent:guid];
39 |
40 | NSURL *URL = [NSURL URLWithString:@"http://example.com/xmlrpc"];
41 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
42 | [request setHTTPMethod:@"POST"];
43 |
44 | NSInputStream *fileStream = [NSInputStream inputStreamWithFileAtPath:filePath];
45 | WPXMLRPCEncoder *encoder = [[WPXMLRPCEncoder alloc] initWithMethod:@"test.uploadFile" andParameters:@[fileStream]];
46 |
47 | [encoder encodeToFile:streamingCacheFilePath error:nil];
48 |
49 | NSError *error = nil;
50 | NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];
51 | unsigned long contentLength = [[attributes objectForKey:NSFileSize] unsignedIntegerValue];
52 | NSInputStream * inputStream = [NSInputStream inputStreamWithFileAtPath:filePath];
53 |
54 | [request setHTTPBodyStream:inputStream];
55 | [request setValue:[NSString stringWithFormat:@"%lu", contentLength] forHTTPHeaderField:@"Content-Length"];
56 |
57 | ## Parsing a XML-RPC response
58 |
59 | NSData *responseData = …
60 | WPXMLRPCDecoder *decoder = [[WPXMLRPCDecoder alloc] initWithData:responseData];
61 | if ([decoder isFault]) {
62 | NSLog(@"XML-RPC error %@: %@", [decoder faultCode], [decoder faultString]);
63 | } else {
64 | NSLog(@"XML-RPC response: %@", [decoder object]);
65 | }
66 |
67 | # Acknowledgments
68 |
69 | The Base64 encoder/decoder found in NSData+Base64 is created by [Matt Gallagher](http://cocoawithlove.com/2009/06/base64-encoding-options-on-mac-and.html).
70 |
71 | The original Cocoa XML-RPC Framework was developed by [Eric Czarny](https://github.com/eczarny/xmlrpc) and now lives at [github.com/corristo/xmlrpc](https://github.com/corristo/xmlrpc)
72 |
--------------------------------------------------------------------------------
/Support/Info-Tests.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Support/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/WPXMLRPC/WPBase64Utils.h:
--------------------------------------------------------------------------------
1 | // WPBase64Utils.h
2 | //
3 | // Copyright (c) 2013 WordPress - http://wordpress.org/
4 | // Based on Eric Czarny's xmlrpc library - https://github.com/eczarny/xmlrpc
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import
25 |
26 | @interface WPBase64Utils : NSObject
27 | + (NSString *)encodeData:(NSData *)data;
28 | + (void)encodeInputStream:(NSInputStream *)stream withChunkHandler:(void (^)(NSString *chunk))chunkHandler;
29 | + (void)encodeFileHandle:(NSFileHandle *)fileHandle withChunkHandler:(void (^)(NSString *chunk))chunkHandler;
30 | + (NSData *)decodeString:(NSString *)string;
31 | @end
32 |
--------------------------------------------------------------------------------
/WPXMLRPC/WPBase64Utils.m:
--------------------------------------------------------------------------------
1 | // WPBase64Utils.m
2 | //
3 | // Copyright (c) 2013 WordPress - http://wordpress.org/
4 | // Based on Eric Czarny's xmlrpc library - https://github.com/eczarny/xmlrpc
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "WPBase64Utils.h"
25 |
26 | // Base64 encoding/decoding functions created by Matt Gallagher on 2009/06/03.
27 | // Copyright 2009 Matt Gallagher. All rights reserved.
28 | //
29 | // This software is provided 'as-is', without any express or implied
30 | // warranty. In no event will the authors be held liable for any damages
31 | // arising from the use of this software. Permission is granted to anyone to
32 | // use this software for any purpose, including commercial applications, and to
33 | // alter it and redistribute it freely, subject to the following restrictions:
34 | //
35 | // 1. The origin of this software must not be misrepresented; you must not
36 | // claim that you wrote the original software. If you use this software
37 | // in a product, an acknowledgment in the product documentation would be
38 | // appreciated but is not required.
39 | // 2. Altered source versions must be plainly marked as such, and must not be
40 | // misrepresented as being the original software.
41 | // 3. This notice may not be removed or altered from any source
42 | // distribution.
43 | //
44 |
45 |
46 | //
47 | // Mapping from 6 bit pattern to ASCII character.
48 | //
49 | static unsigned char base64EncodeLookup[65] =
50 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
51 |
52 | //
53 | // Definition for "masked-out" areas of the base64DecodeLookup mapping
54 | //
55 | #define xx 65
56 |
57 | //
58 | // Mapping from ASCII character to 6 bit pattern.
59 | //
60 | static unsigned char base64DecodeLookup[256] =
61 | {
62 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
63 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
64 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 62, xx, xx, xx, 63,
65 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, xx, xx, xx, xx, xx, xx,
66 | xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
67 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, xx, xx, xx, xx, xx,
68 | xx, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
69 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, xx, xx, xx, xx, xx,
70 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
71 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
72 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
73 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
74 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
75 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
76 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
77 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
78 | };
79 |
80 | //
81 | // Fundamental sizes of the binary and base64 encode/decode units in bytes
82 | //
83 | #define BINARY_UNIT_SIZE 3
84 | #define BASE64_UNIT_SIZE 4
85 |
86 | //
87 | // NewBase64Decode
88 | //
89 | // Decodes the base64 ASCII string in the inputBuffer to a newly malloced
90 | // output buffer.
91 | //
92 | // inputBuffer - the source ASCII string for the decode
93 | // length - the length of the string or -1 (to specify strlen should be used)
94 | // outputLength - if not-NULL, on output will contain the decoded length
95 | //
96 | // returns the decoded buffer. Must be free'd by caller. Length is given by
97 | // outputLength.
98 | //
99 | void *NewBase64Decode(
100 | const char *inputBuffer,
101 | size_t length,
102 | size_t *outputLength)
103 | {
104 | if (length == -1)
105 | {
106 | length = strlen(inputBuffer);
107 | }
108 |
109 | size_t outputBufferSize =
110 | ((length+BASE64_UNIT_SIZE-1) / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE;
111 | unsigned char *outputBuffer = (unsigned char *)malloc(outputBufferSize);
112 |
113 | size_t i = 0;
114 | size_t j = 0;
115 | while (i < length)
116 | {
117 | //
118 | // Accumulate 4 valid characters (ignore everything else)
119 | //
120 | unsigned char accumulated[BASE64_UNIT_SIZE];
121 | size_t accumulateIndex = 0;
122 | while (i < length)
123 | {
124 | unsigned char decode = base64DecodeLookup[inputBuffer[i++]];
125 | if (decode != xx)
126 | {
127 | accumulated[accumulateIndex] = decode;
128 | accumulateIndex++;
129 |
130 | if (accumulateIndex == BASE64_UNIT_SIZE)
131 | {
132 | break;
133 | }
134 | }
135 | }
136 |
137 | //
138 | // Store the 6 bits from each of the 4 characters as 3 bytes
139 | //
140 | // (Uses improved bounds checking suggested by Alexandre Colucci)
141 | //
142 | if(accumulateIndex >= 2)
143 | outputBuffer[j] = (accumulated[0] << 2) | (accumulated[1] >> 4);
144 | if(accumulateIndex >= 3)
145 | outputBuffer[j + 1] = (accumulated[1] << 4) | (accumulated[2] >> 2);
146 | if(accumulateIndex >= 4)
147 | outputBuffer[j + 2] = (accumulated[2] << 6) | accumulated[3];
148 |
149 | // We should increment j only with a positive accumulateIndex to prevent integer overflow
150 | if (accumulateIndex > 0)
151 | j += accumulateIndex - 1;
152 | }
153 |
154 | if (outputLength)
155 | {
156 | *outputLength = j;
157 | }
158 | return outputBuffer;
159 | }
160 |
161 | //
162 | // NewBase64Encode
163 | //
164 | // Encodes the arbitrary data in the inputBuffer as base64 into a newly malloced
165 | // output buffer.
166 | //
167 | // inputBuffer - the source data for the encode
168 | // length - the length of the input in bytes
169 | // separateLines - if zero, no CR/LF characters will be added. Otherwise
170 | // a CR/LF pair will be added every 64 encoded chars.
171 | // outputLength - if not-NULL, on output will contain the encoded length
172 | // (not including terminating 0 char)
173 | //
174 | // returns the encoded buffer. Must be free'd by caller. Length is given by
175 | // outputLength.
176 | //
177 | char *NewBase64Encode(
178 | const void *buffer,
179 | size_t length,
180 | bool separateLines,
181 | size_t *outputLength)
182 | {
183 | const unsigned char *inputBuffer = (const unsigned char *)buffer;
184 |
185 | #define MAX_NUM_PADDING_CHARS 2
186 | #define OUTPUT_LINE_LENGTH 64
187 | #define INPUT_LINE_LENGTH ((OUTPUT_LINE_LENGTH / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE)
188 | #define CR_LF_SIZE 2
189 |
190 | //
191 | // Byte accurate calculation of final buffer size
192 | //
193 | size_t outputBufferSize =
194 | ((length / BINARY_UNIT_SIZE)
195 | + ((length % BINARY_UNIT_SIZE) ? 1 : 0))
196 | * BASE64_UNIT_SIZE;
197 | if (separateLines)
198 | {
199 | outputBufferSize +=
200 | (outputBufferSize / OUTPUT_LINE_LENGTH) * CR_LF_SIZE;
201 | }
202 |
203 | //
204 | // Include space for a terminating zero
205 | //
206 | outputBufferSize += 1;
207 |
208 | //
209 | // Allocate the output buffer
210 | //
211 | char *outputBuffer = (char *)malloc(outputBufferSize);
212 | if (!outputBuffer)
213 | {
214 | return NULL;
215 | }
216 |
217 | size_t i = 0;
218 | size_t j = 0;
219 | const size_t lineLength = separateLines ? INPUT_LINE_LENGTH : length;
220 | size_t lineEnd = lineLength;
221 |
222 | while (true)
223 | {
224 | if (lineEnd > length)
225 | {
226 | lineEnd = length;
227 | }
228 |
229 | for (; i + BINARY_UNIT_SIZE - 1 < lineEnd; i += BINARY_UNIT_SIZE)
230 | {
231 | //
232 | // Inner loop: turn 48 bytes into 64 base64 characters
233 | //
234 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
235 | outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4)
236 | | ((inputBuffer[i + 1] & 0xF0) >> 4)];
237 | outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i + 1] & 0x0F) << 2)
238 | | ((inputBuffer[i + 2] & 0xC0) >> 6)];
239 | outputBuffer[j++] = base64EncodeLookup[inputBuffer[i + 2] & 0x3F];
240 | }
241 |
242 | if (lineEnd == length)
243 | {
244 | break;
245 | }
246 |
247 | //
248 | // Add the newline
249 | //
250 | outputBuffer[j++] = '\r';
251 | outputBuffer[j++] = '\n';
252 | lineEnd += lineLength;
253 | }
254 |
255 | if (i + 1 < length)
256 | {
257 | //
258 | // Handle the single '=' case
259 | //
260 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
261 | outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4)
262 | | ((inputBuffer[i + 1] & 0xF0) >> 4)];
263 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i + 1] & 0x0F) << 2];
264 | outputBuffer[j++] = '=';
265 | }
266 | else if (i < length)
267 | {
268 | //
269 | // Handle the double '=' case
270 | //
271 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
272 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0x03) << 4];
273 | outputBuffer[j++] = '=';
274 | outputBuffer[j++] = '=';
275 | }
276 | outputBuffer[j] = 0;
277 |
278 | //
279 | // Set the output length and return the buffer
280 | //
281 | if (outputLength)
282 | {
283 | *outputLength = j;
284 | }
285 | return outputBuffer;
286 | }
287 |
288 | #pragma mark -
289 |
290 | // If you change this, be sure it's a multiple of 90.
291 | // The base 64 encoding adds a new line every 90 characters.
292 | NSUInteger const WPBase64UtilsChunkSize = 18000;
293 |
294 | @implementation WPBase64Utils
295 | + (NSString *)encodeData:(NSData *)data {
296 | size_t outputLength;
297 | char *outputBuffer = NewBase64Encode([data bytes], [data length], false, &outputLength);
298 |
299 | NSString *result = [[NSString alloc] initWithBytes:outputBuffer
300 | length:outputLength
301 | encoding:NSASCIIStringEncoding];
302 |
303 | free(outputBuffer);
304 | return result;
305 | }
306 |
307 | + (void)encodeInputStream:(NSInputStream *)stream withChunkHandler:(void (^)(NSString *chunk))chunkHandler {
308 | [stream open];
309 |
310 | while ([stream hasBytesAvailable]) {
311 | uint8_t buf[WPBase64UtilsChunkSize];
312 | NSInteger len = 0;
313 |
314 | len = [stream read:buf maxLength:WPBase64UtilsChunkSize];
315 | if (len) {
316 | @autoreleasepool {
317 | NSData *chunk = [NSData dataWithBytes:buf length:len];
318 | NSString *encodedChunk = [self encodeData:chunk];
319 | chunkHandler(encodedChunk);
320 | }
321 | }
322 | }
323 |
324 | [stream close];
325 | }
326 |
327 | + (void)encodeFileHandle:(NSFileHandle *)fileHandle withChunkHandler:(void (^)(NSString *chunk))chunkHandler {
328 | NSData *chunk = [fileHandle readDataOfLength:WPBase64UtilsChunkSize];
329 | while ([chunk length] > 0) {
330 | NSString *encodedChunk = [self encodeData:chunk];
331 | chunkHandler(encodedChunk);
332 | chunk = [fileHandle readDataOfLength:WPBase64UtilsChunkSize];
333 | }
334 | }
335 |
336 | + (NSData *)decodeString:(NSString *)string {
337 | NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding];
338 | size_t outputLength;
339 | void *outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength);
340 | NSData *result = [NSData dataWithBytes:outputBuffer length:outputLength];
341 | free(outputBuffer);
342 | return result;
343 | }
344 | @end
345 |
--------------------------------------------------------------------------------
/WPXMLRPC/WPStringUtils.h:
--------------------------------------------------------------------------------
1 | // WPStringUtils.h
2 | //
3 | // Copyright (c) 2013 WordPress - http://wordpress.org/
4 | // Based on Eric Czarny's xmlrpc library - https://github.com/eczarny/xmlrpc
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import
25 |
26 | @interface WPStringUtils : NSObject
27 | + (NSString *)unescapedStringWithString:(NSString *)string;
28 | + (NSString *)escapedStringWithString:(NSString *)string;
29 | @end
30 |
--------------------------------------------------------------------------------
/WPXMLRPC/WPStringUtils.m:
--------------------------------------------------------------------------------
1 | // WPStringUtils.m
2 | //
3 | // Copyright (c) 2013 WordPress - http://wordpress.org/
4 | // Based on Eric Czarny's xmlrpc library - https://github.com/eczarny/xmlrpc
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "WPStringUtils.h"
25 |
26 | @implementation WPStringUtils
27 |
28 | + (NSString *)unescapedStringWithString:(NSString *)aString {
29 | if (!aString) {
30 | return nil;
31 | }
32 |
33 | NSMutableString *string = [NSMutableString stringWithString:aString];
34 |
35 | [string replaceOccurrencesOfString:@""" withString:@"\"" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
36 | [string replaceOccurrencesOfString:@"'" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
37 | [string replaceOccurrencesOfString:@"9" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
38 | [string replaceOccurrencesOfString:@"" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
39 | [string replaceOccurrencesOfString:@"" withString:@"'" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
40 | [string replaceOccurrencesOfString:@">" withString:@">" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
41 | [string replaceOccurrencesOfString:@"<" withString:@"<" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
42 | [string replaceOccurrencesOfString:@"&" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
43 |
44 | return [NSString stringWithString:string];
45 | }
46 |
47 | + (NSString *)escapedStringWithString:(NSString *)aString {
48 | if (!aString) {
49 | return nil;
50 | }
51 |
52 | NSMutableString *string = [NSMutableString stringWithString:aString];
53 |
54 | // NOTE:we use unicode entities instead of & > < etc. since some hosts (powweb, fatcow, and similar)
55 | // have a weird PHP/libxml2 combination that ignores regular entities
56 | [string replaceOccurrencesOfString:@"&" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
57 | [string replaceOccurrencesOfString:@">" withString:@">" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
58 | [string replaceOccurrencesOfString:@"<" withString:@"<" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
59 |
60 | return [NSString stringWithString:string];
61 | }
62 |
63 | @end
--------------------------------------------------------------------------------
/WPXMLRPC/WPXMLRPC.h:
--------------------------------------------------------------------------------
1 | // WPXMLRPC.h
2 | //
3 | // Copyright (c) 2013 WordPress - http://wordpress.org/
4 | // Based on Eric Czarny's xmlrpc library - https://github.com/eczarny/xmlrpc
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import
25 | #ifndef _WPXMLRPC
26 | #define _WPXMLRPC
27 | #import "WPXMLRPCEncoder.h"
28 | #import "WPXMLRPCDecoder.h"
29 | #endif /* _WPXMLRPC */
30 |
--------------------------------------------------------------------------------
/WPXMLRPC/WPXMLRPCDataCleaner.h:
--------------------------------------------------------------------------------
1 | // WPXMLRPCDataCleaner.h
2 | // Based on code from WordPress for iOS http://ios.wordpress.org/
3 | //
4 | // Copyright (c) 2013 WordPress - http://wordpress.org/
5 | // Based on Eric Czarny's xmlrpc library - https://github.com/eczarny/xmlrpc
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | #import
26 |
27 | @interface WPXMLRPCDataCleaner : NSObject
28 | - (id)initWithData:(NSData *)data;
29 |
30 | - (NSData *)cleanData;
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/WPXMLRPC/WPXMLRPCDataCleaner.m:
--------------------------------------------------------------------------------
1 | // WPXMLRPCDataCleaner.m
2 | // Based on code from WordPress for iOS http://ios.wordpress.org/
3 | //
4 | // Copyright (c) 2013 WordPress - http://wordpress.org/
5 | // Based on Eric Czarny's xmlrpc library - https://github.com/eczarny/xmlrpc
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 |
25 | #import "WPXMLRPCDataCleaner.h"
26 | #import
27 |
28 | @interface WPXMLRPCDataCleaner (CleaningSteps)
29 | - (NSData *)cleanInvalidUTF8:(NSData *)str;
30 | - (NSString *)cleanCharactersBeforePreamble:(NSString *)str;
31 | - (NSString *)cleanInvalidXMLCharacters:(NSString *)str;
32 | - (NSString *)cleanWithTidyIfPresent:(NSString *)str;
33 | - (NSString *)cleanClosingTagIfNeeded:(NSString *)str lengthOfCharactersPrecedingPreamble:(NSInteger)length;
34 | @end
35 |
36 | @implementation WPXMLRPCDataCleaner {
37 | NSData *xmlData;
38 | }
39 |
40 | #pragma mark - initializers
41 |
42 | - (id)initWithData:(NSData *)data {
43 | self = [super init];
44 | if (self) {
45 | xmlData = data;
46 | }
47 | return self;
48 | }
49 |
50 |
51 | #pragma mark - clean output
52 |
53 | - (NSData *)cleanData {
54 | if (xmlData == nil)
55 | return nil;
56 |
57 | NSData *cleanData = [self cleanInvalidUTF8:xmlData];
58 | NSString *cleanString = [[NSString alloc] initWithData:cleanData encoding:NSUTF8StringEncoding];
59 |
60 | if (cleanString == nil) {
61 | // Although it shouldn't happen, fall back to Latin1 if data is not proper UTF-8
62 | cleanString = [[NSString alloc] initWithData:cleanData encoding:NSISOLatin1StringEncoding];
63 | }
64 | NSInteger startinglength = [cleanString length];
65 | cleanString = [self cleanCharactersBeforePreamble:cleanString];
66 | NSInteger len = startinglength - [cleanString length];
67 | cleanString = [self cleanInvalidXMLCharacters:cleanString];
68 | cleanString = [self cleanWithTidyIfPresent:cleanString];
69 |
70 | cleanString = [self cleanClosingTagIfNeeded:cleanString lengthOfCharactersPrecedingPreamble:len];
71 |
72 | cleanData = [cleanString dataUsingEncoding:NSUTF8StringEncoding];
73 |
74 | return cleanData;
75 | }
76 |
77 | @end
78 |
79 | @implementation WPXMLRPCDataCleaner (CleaningSteps)
80 |
81 | /**
82 | Runs the given data through iconv to discard any invalid UTF-8 characters
83 | */
84 | - (NSData *)cleanInvalidUTF8:(NSData *)data {
85 | NSData *result;
86 | iconv_t cd = iconv_open("UTF-8", "UTF-8"); // convert to UTF-8 from UTF-8
87 | int one = 1;
88 | iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, &one); // discard invalid characters
89 |
90 | size_t inbytesleft, outbytesleft;
91 | inbytesleft = outbytesleft = data.length;
92 |
93 | char *inbuf = (char *)data.bytes;
94 | char *outbuf = malloc(sizeof(char) * data.length);
95 | char *outptr = outbuf;
96 |
97 | if (iconv(cd, &inbuf, &inbytesleft, &outptr, &outbytesleft) == (size_t)-1) {
98 | // Failed iconv, possible errors to encounter in `errno`:
99 | //
100 | // E2BIG - There is not sufficient room at *outbuf.
101 | // EILSEQ - An invalid multibyte sequence has been encountered in the input.
102 | // EINVAL - An incomplete multibyte sequence has been encountered in the input.
103 | //
104 | // It should never happen since we've told iconv to discard anything invalid
105 |
106 | result = data;
107 | } else {
108 | result = [NSData dataWithBytes:outbuf length:data.length - outbytesleft];
109 | }
110 |
111 | iconv_close(cd);
112 | free(outbuf);
113 |
114 | return result;
115 | }
116 |
117 | /**
118 | Remove any text before the XML preamble ` 0 && range.location > 0) {
124 | str = [str substringFromIndex:range.location];
125 | }
126 | return str;
127 | }
128 |
129 | /**
130 | Remove invalid characters as specified by the XML 1.0 standard
131 |
132 | Based on http://benjchristensen.com/2008/02/07/how-to-strip-invalid-xml-characters/
133 | */
134 | - (NSString *)cleanInvalidXMLCharacters:(NSString *)str {
135 | NSUInteger len = [str length];
136 |
137 | NSMutableString *result = [NSMutableString stringWithCapacity:len];
138 | for( int charIndex = 0; charIndex < len; charIndex++) {
139 | unichar testChar = [str characterAtIndex:charIndex];
140 | if((testChar == 0x9) ||
141 | (testChar == 0xA) ||
142 | (testChar == 0xD) ||
143 | ((testChar >= 0x20) && (testChar <= 0xD7FF)) ||
144 | ((testChar >= 0xE000) && (testChar <= 0xFFFD))
145 | ) {
146 | NSString *validCharacter = [NSString stringWithFormat:@"%C", testChar];
147 | [result appendString:validCharacter];
148 | }
149 | }
150 | return result;
151 | }
152 |
153 | /**
154 | If CTidy is available, run the cleaned XML through tidy as a last fix before parsing
155 | */
156 | - (NSString *)cleanWithTidyIfPresent:(NSString *)str {
157 | /*
158 | The conditional code we're executing is the equivalent of:
159 |
160 | [[CTidy tidy] tidyString:str inputFormat:TidyFormat_XML outputFormat:TidyFormat_XML diagnostics:NULL error:&err];
161 | */
162 | id _CTidyClass = NSClassFromString(@"CTidy");
163 | SEL _CTidySelector = NSSelectorFromString(@"tidy");
164 | SEL _CTidyTidyStringSelector = NSSelectorFromString(@"tidyString:inputFormat:outputFormat:encoding:diagnostics:error:");
165 |
166 | if (_CTidyClass && [_CTidyClass respondsToSelector:_CTidySelector]) {
167 | #pragma clang diagnostic push
168 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
169 | id _CTidyInstance = [_CTidyClass performSelector:_CTidySelector];
170 | #pragma clang diagnostic pop
171 |
172 | if (_CTidyInstance && [_CTidyInstance respondsToSelector:_CTidyTidyStringSelector]) {
173 | typedef NSString *(*_CTidyTidyStringMethodType)(id, SEL, NSString *, int, int, NSString *, NSError **);
174 | _CTidyTidyStringMethodType _CTidyTidyStringMethod;
175 | _CTidyTidyStringMethod = (_CTidyTidyStringMethodType)[_CTidyInstance methodForSelector:_CTidyTidyStringSelector];
176 |
177 | NSError *err = nil;
178 | NSString *result = _CTidyTidyStringMethod(_CTidyInstance, _CTidyTidyStringSelector, str, 1, 1, @"utf8", &err);
179 |
180 | if (result)
181 | return result;
182 | }
183 | }
184 |
185 | // If we reach this point, something failed. Return the original string
186 | return str;
187 | }
188 |
189 | /**
190 | In certain situations the xml response can be truncated due to an inaccurate
191 | content-length header. This can happen, for example, due to a BOM or whitespace
192 | preceeding an opening php tag. In these cases the xmlrpc response can be truncated
193 | the length of these extra leading characters.
194 | Check for a good closing tag, and try to repair a broken closing tag.
195 | */
196 | - (NSString *)cleanClosingTagIfNeeded:(NSString *)str lengthOfCharactersPrecedingPreamble:(NSInteger)length
197 | {
198 | // Clean responses, but not requests (for now).
199 | if ([str rangeOfString:@"methodResponse"].location == NSNotFound) {
200 | return str;
201 | }
202 |
203 | // Check for breakage.
204 | if ([str rangeOfString:@""].location != NSNotFound) {
205 | // Nothing broken. All is well.
206 | return str;
207 | }
208 |
209 | // Get the proper closing tags for the request.
210 | NSString *closingTags = [self cloingTagsForString:str];
211 |
212 | // If the length of the content cleaned from before the preamble is greater
213 | // than the length of the closing tags, then the xml is (probably) too damaged to repair.
214 | if (length > [closingTags length]) {
215 | // we can't fix this
216 | return str;
217 | }
218 |
219 | // Repair a truncated reponse where the ending markup is a partial match to
220 | // the closing tags. This should be the majority of cases.
221 | NSString *repairedStr = [self repairTruncatedClosingTags:closingTags inResponseString:str];
222 | if (repairedStr) {
223 | return repairedStr;
224 | }
225 |
226 | // No partial match found so append the closing tags safely.
227 | repairedStr = [self appendClosingTags:closingTags toResponseString:str];
228 | return repairedStr;
229 | }
230 |
231 | - (NSString *)cloingTagsForString:(NSString *)str
232 | {
233 | if ([str rangeOfString:@""].location != NSNotFound) {
234 | return @" ";
235 | } else {
236 | return @"";
237 | }
238 | }
239 |
240 | - (NSRange)rangeOfLastValidClosingTagInString:(NSString *)str
241 | {
242 | static NSRegularExpression *regex;
243 | static dispatch_once_t onceToken;
244 | dispatch_once(&onceToken, ^{
245 | NSError *error;
246 | regex = [NSRegularExpression regularExpressionWithPattern:@"[^>]+>[^>]*$" options:NSRegularExpressionCaseInsensitive error:&error];
247 | });
248 |
249 | return [regex rangeOfFirstMatchInString:str options:NSMatchingReportCompletion range:NSMakeRange(0, [str length])];
250 | }
251 |
252 | - (NSString *)repairTruncatedClosingTags:(NSString *)closingTags inResponseString:(NSString *)str
253 | {
254 | // Find the last valid closing tag.
255 | NSRange rangeOfLastValidTag = [self rangeOfLastValidClosingTagInString:str];
256 | if (rangeOfLastValidTag.location == NSNotFound) {
257 | // There was no valid closing tag to be found. Strange!
258 | return str;
259 | }
260 | NSString *lastClosingTag = [str substringWithRange:rangeOfLastValidTag];
261 | NSRange range = [closingTags rangeOfString:lastClosingTag];
262 | if (range.location == NSNotFound) {
263 | return nil;
264 | }
265 |
266 | // Partial match found
267 | NSString *s1 = [str substringToIndex:rangeOfLastValidTag.location]; // get the string up to the start of the last closing tag.
268 | NSString *s2 = [closingTags substringFromIndex:range.location];
269 | return [NSString stringWithFormat:@"%@%@", s1, s2];
270 | }
271 |
272 | - (NSString *)appendClosingTags:(NSString *)closingTags toResponseString:(NSString *)str
273 | {
274 | // Find the start of the last end tag
275 | NSRange range = [str rangeOfString:@"<" options:NSBackwardsSearch];
276 | if (range.location == NSNotFound) {
277 | // This should never happen, but handle the one in a million case.
278 | return str;
279 | }
280 |
281 | // Find the ending stub, if there is one, as a safety precaution.
282 | NSInteger index = 0;
283 | NSString *stub = [str substringFromIndex:range.location];
284 | range = [closingTags rangeOfString:stub];
285 | if (range.location != NSNotFound) {
286 | index = range.location + range.length;
287 | }
288 |
289 | // Append the missing part of the closing tags
290 | return [NSString stringWithFormat:@"%@%@", str, [closingTags substringFromIndex:index]];
291 |
292 | }
293 |
294 | @end
--------------------------------------------------------------------------------
/WPXMLRPC/WPXMLRPCDecoder.h:
--------------------------------------------------------------------------------
1 | // WPXMLRPCDecoder.h
2 | //
3 | // Copyright (c) 2013 WordPress - http://wordpress.org/
4 | // Based on Eric Czarny's xmlrpc library - https://github.com/eczarny/xmlrpc
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import
25 |
26 | NS_ASSUME_NONNULL_BEGIN
27 |
28 | extern NSString * const WPXMLRPCFaultErrorDomain;
29 | extern NSString * const WPXMLRPCErrorDomain;
30 |
31 | typedef NS_ENUM(NSInteger, WPXMLRPCError) {
32 | WPXMLRPCInvalidInputError, // The data passed doesn't look like a XML-RPC response
33 | WPXMLRPCOutOfMemoryError, // Response was a PHP out of memory fatal error
34 | };
35 |
36 | /**
37 | `WPXMLRPCEncoder` encodes a XML-RPC response
38 | */
39 | @interface WPXMLRPCDecoder : NSObject
40 |
41 | /**
42 | Initializes a `WPXMLRPCDecoder` object with the specified response data.
43 |
44 | @param data the data returned by your XML-RPC request
45 |
46 | @return The newly-initialized XML-RPC response
47 | */
48 | - (nullable instancetype)initWithData:(NSData *)data;
49 |
50 | ///-----------------------
51 | /// @name Error management
52 | ///-----------------------
53 |
54 | /**
55 | Returns YES if the response contains a XML-RPC error
56 | */
57 | - (BOOL)isFault;
58 |
59 | /**
60 | The XML-RPC error code
61 | */
62 | - (NSInteger)faultCode;
63 |
64 | /**
65 | The XML-RPC error message
66 | */
67 | - (nullable NSString *)faultString;
68 |
69 | /**
70 | Returns an error if there was a problem decoding the data, or if it's a XML-RPC error
71 | */
72 | - (nullable NSError *)error;
73 |
74 | ///-------------------------------------
75 | /// @name Accessing the decoded response
76 | ///-------------------------------------
77 |
78 | /**
79 | The decoded object
80 |
81 | Check isFault before trying to do anything with this object.
82 | */
83 | - (nullable id)object;
84 |
85 | @end
86 |
87 | NS_ASSUME_NONNULL_END
88 |
--------------------------------------------------------------------------------
/WPXMLRPC/WPXMLRPCDecoder.m:
--------------------------------------------------------------------------------
1 | // WPXMLRPCDecoder.m
2 | //
3 | // Copyright (c) 2013 WordPress - http://wordpress.org/
4 | // Based on Eric Czarny's xmlrpc library - https://github.com/eczarny/xmlrpc
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "WPXMLRPCDecoder.h"
25 | #import "WPXMLRPCDecoderDelegate.h"
26 | #import "WPXMLRPCDataCleaner.h"
27 |
28 | NSString *const WPXMLRPCFaultErrorDomain = @"WPXMLRPCFaultError";
29 | NSString *const WPXMLRPCErrorDomain = @"WPXMLRPCError";
30 |
31 | @interface WPXMLRPCDecoder ()
32 | @end
33 |
34 | @implementation WPXMLRPCDecoder {
35 | NSXMLParser *_parser;
36 | WPXMLRPCDecoderDelegate *_delegate;
37 | BOOL _isFault;
38 | NSData *_body;
39 | NSData *_originalData;
40 | id _object;
41 | NSMutableString *_methodName;
42 | }
43 |
44 | - (instancetype)initWithData:(NSData *)data {
45 | if (!data) {
46 | return nil;
47 | }
48 |
49 | if (self = [self init]) {
50 | _body = data;
51 | _originalData = data;
52 | _parser = [[NSXMLParser alloc] initWithData:data];
53 | _delegate = nil;
54 | _isFault = NO;
55 | [self parse];
56 | }
57 |
58 | return self;
59 | }
60 |
61 | #pragma mark -
62 |
63 | - (void)parse {
64 | [_parser setDelegate:self];
65 |
66 | [_parser parse];
67 |
68 | if ([_parser parserError]) {
69 | WPXMLRPCDataCleaner *cleaner = [[WPXMLRPCDataCleaner alloc] initWithData:_originalData];
70 | _body = [cleaner cleanData];
71 | _parser = [[NSXMLParser alloc] initWithData:_body];
72 | [_parser setDelegate:self];
73 | [_parser parse];
74 | }
75 |
76 | if ([_parser parserError])
77 | return;
78 |
79 | if (_methodName) {
80 | _object = @{@"methodName": _methodName, @"params": [_delegate elementValue]};
81 | } else {
82 | _object = [_delegate elementValue];
83 | }
84 | }
85 |
86 | - (void)abortParsing {
87 | [_parser abortParsing];
88 | }
89 |
90 | #pragma mark -
91 |
92 | - (BOOL)isFault {
93 | return _isFault;
94 | }
95 |
96 | - (NSInteger)faultCode {
97 | if ([self isFault]) {
98 | return [[_object objectForKey: @"faultCode"] integerValue];
99 | }
100 |
101 | return 0;
102 | }
103 |
104 | - (NSString *)faultString {
105 | if ([self isFault]) {
106 | return [_object objectForKey: @"faultString"];
107 | }
108 |
109 | return nil;
110 | }
111 |
112 | - (NSError *)error {
113 | if ([_parser parserError]) {
114 | NSString *response = [[NSString alloc] initWithData:_originalData encoding:NSUTF8StringEncoding];
115 | if (response) {
116 | // Search for known PHP errors
117 | NSError *error = NULL;
118 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"Allowed memory size of \\d+ bytes exhausted" options:NSRegularExpressionCaseInsensitive error:&error];
119 | NSUInteger numberOfMatches = [regex numberOfMatchesInString:response options:0 range:NSMakeRange(0, [response length])];
120 | if (numberOfMatches > 0) {
121 | return [NSError errorWithDomain:WPXMLRPCErrorDomain code:WPXMLRPCOutOfMemoryError userInfo:@{NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"Your site ran out of memory while processing this request", @"WPXMLRPC", nil)}];
122 | }
123 | }
124 | return [_parser parserError];
125 | }
126 |
127 | if ([self isFault]) {
128 | if ([self faultString] == nil || [_object objectForKey: @"faultCode"] == nil) {
129 | return [NSError errorWithDomain:WPXMLRPCErrorDomain
130 | code:WPXMLRPCInvalidInputError
131 | userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"The data doesn't look like a valid XML-RPC Fault response", @"WPXMLRPC failed to read fault response from server")}];
132 | } else {
133 | return [NSError errorWithDomain:WPXMLRPCFaultErrorDomain code:[self faultCode] userInfo:@{NSLocalizedDescriptionKey: [self faultString]}];
134 | }
135 | }
136 |
137 | if (_object == nil) {
138 | return [NSError errorWithDomain:WPXMLRPCErrorDomain code:WPXMLRPCInvalidInputError userInfo:@{NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"The data doesn't look like a valid XML-RPC response", @"WPXMLRPC", nil)}];
139 | }
140 |
141 | return nil;
142 | }
143 |
144 | #pragma mark -
145 |
146 | - (id)object {
147 | return _object;
148 | }
149 |
150 | @end
151 |
152 | #pragma mark -
153 |
154 | @implementation WPXMLRPCDecoder (NSXMLParserDelegate)
155 |
156 | - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)element namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributes {
157 | if ([element isEqualToString:@"fault"]) {
158 | _isFault = YES;
159 | } else if ([element isEqualToString:@"value"]) {
160 | _delegate = [[WPXMLRPCDecoderDelegate alloc] initWithParent:nil];
161 |
162 | [_parser setDelegate:_delegate];
163 | } else if ([element isEqualToString:@"methodName"]) {
164 | _methodName = [NSMutableString string];
165 | }
166 | }
167 |
168 | - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
169 | if ([elementName isEqualToString:@"methodName"]) {
170 | _delegate = [[WPXMLRPCDecoderDelegate alloc] initWithParent:nil];
171 |
172 | [_parser setDelegate:_delegate];
173 | }
174 | }
175 |
176 | - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
177 | [_methodName appendString:string];
178 | }
179 |
180 | - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
181 | [self abortParsing];
182 | }
183 |
184 | @end
185 |
--------------------------------------------------------------------------------
/WPXMLRPC/WPXMLRPCDecoderDelegate.h:
--------------------------------------------------------------------------------
1 | // WPXMLRPCDecoderDelegate.h
2 | //
3 | // Copyright (c) 2013 WordPress - http://wordpress.org/
4 | // Based on Eric Czarny's xmlrpc library - https://github.com/eczarny/xmlrpc
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import
25 |
26 | typedef enum {
27 | WPXMLRPCElementTypeArray,
28 | WPXMLRPCElementTypeDictionary,
29 | WPXMLRPCElementTypeMember,
30 | WPXMLRPCElementTypeName,
31 | WPXMLRPCElementTypeInteger,
32 | WPXMLRPCElementTypeDouble,
33 | WPXMLRPCElementTypeBoolean,
34 | WPXMLRPCElementTypeString,
35 | WPXMLRPCElementTypeDate,
36 | WPXMLRPCElementTypeData
37 | } WPXMLRPCElementType;
38 |
39 | #pragma mark -
40 |
41 | @interface WPXMLRPCDecoderDelegate : NSObject {
42 | __weak WPXMLRPCDecoderDelegate *myParent;
43 | NSMutableArray *myChildren;
44 | WPXMLRPCElementType myElementType;
45 | NSString *myElementKey;
46 | id myElementValue;
47 | }
48 |
49 | - (id)initWithParent:(WPXMLRPCDecoderDelegate *)parent;
50 |
51 | #pragma mark -
52 |
53 | - (void)setParent:(WPXMLRPCDecoderDelegate *)parent;
54 |
55 | - (WPXMLRPCDecoderDelegate *)parent;
56 |
57 | #pragma mark -
58 |
59 | - (void)setElementType:(WPXMLRPCElementType)elementType;
60 |
61 | - (WPXMLRPCElementType)elementType;
62 |
63 | #pragma mark -
64 |
65 | - (void)setElementKey:(NSString *)elementKey;
66 |
67 | - (NSString *)elementKey;
68 |
69 | #pragma mark -
70 |
71 | - (void)setElementValue:(id)elementValue;
72 |
73 | - (id)elementValue;
74 |
75 | @end
76 |
--------------------------------------------------------------------------------
/WPXMLRPC/WPXMLRPCDecoderDelegate.m:
--------------------------------------------------------------------------------
1 | // WPXMLRPCDecoderDelegate.m
2 | //
3 | // Copyright (c) 2013 WordPress - http://wordpress.org/
4 | // Based on Eric Czarny's xmlrpc library - https://github.com/eczarny/xmlrpc
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "WPXMLRPCDecoderDelegate.h"
25 | #import "WPBase64Utils.h"
26 |
27 | @interface WPXMLRPCDecoderDelegate (WPXMLRPCEventBasedParserDelegatePrivate)
28 |
29 | - (BOOL)isDictionaryElementType:(WPXMLRPCElementType)elementType;
30 |
31 | #pragma mark -
32 |
33 | - (void)addElementValueToParent;
34 |
35 | #pragma mark -
36 |
37 | - (NSDate *)parseDateString:(NSString *)dateString withFormat:(NSString *)format;
38 |
39 | #pragma mark -
40 |
41 | - (NSNumber *)parseInteger:(NSString *)value;
42 |
43 | - (NSNumber *)parseDouble:(NSString *)value;
44 |
45 | - (NSNumber *)parseBoolean:(NSString *)value;
46 |
47 | - (NSString *)parseString:(NSString *)value;
48 |
49 | - (NSDate *)parseDate:(NSString *)value;
50 |
51 | - (NSData *)parseData:(NSString *)value;
52 |
53 | @end
54 |
55 | #pragma mark -
56 |
57 | @implementation WPXMLRPCDecoderDelegate
58 |
59 | - (id)initWithParent:(WPXMLRPCDecoderDelegate *)parent {
60 | self = [super init];
61 | if (self) {
62 | myParent = parent;
63 | myChildren = [[NSMutableArray alloc] initWithCapacity:1];
64 | myElementType = WPXMLRPCElementTypeString;
65 | myElementKey = nil;
66 | myElementValue = [[NSMutableString alloc] init];
67 | }
68 |
69 | return self;
70 | }
71 |
72 | #pragma mark -
73 |
74 | - (void)setParent:(WPXMLRPCDecoderDelegate *)parent {
75 |
76 |
77 | myParent = parent;
78 | }
79 |
80 | - (WPXMLRPCDecoderDelegate *)parent {
81 | return myParent;
82 | }
83 |
84 | #pragma mark -
85 |
86 | - (void)setElementType:(WPXMLRPCElementType)elementType {
87 | myElementType = elementType;
88 | }
89 |
90 | - (WPXMLRPCElementType)elementType {
91 | return myElementType;
92 | }
93 |
94 | #pragma mark -
95 |
96 | - (void)setElementKey:(NSString *)elementKey {
97 |
98 |
99 | myElementKey = elementKey;
100 | }
101 |
102 | - (NSString *)elementKey {
103 | return myElementKey;
104 | }
105 |
106 | #pragma mark -
107 |
108 | - (void)setElementValue:(id)elementValue {
109 |
110 |
111 | myElementValue = elementValue;
112 | }
113 |
114 | - (id)elementValue {
115 | return myElementValue;
116 | }
117 |
118 | #pragma mark -
119 |
120 |
121 | @end
122 |
123 | #pragma mark -
124 |
125 | @implementation WPXMLRPCDecoderDelegate (NSXMLParserDelegate)
126 |
127 | - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)element namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributes {
128 | if ([element isEqualToString:@"value"] || [element isEqualToString:@"member"] || [element isEqualToString:@"name"]) {
129 | WPXMLRPCDecoderDelegate *parserDelegate = [[WPXMLRPCDecoderDelegate alloc] initWithParent:self];
130 |
131 | if ([element isEqualToString:@"member"]) {
132 | [parserDelegate setElementType:WPXMLRPCElementTypeMember];
133 | } else if ([element isEqualToString:@"name"]) {
134 | [parserDelegate setElementType:WPXMLRPCElementTypeName];
135 | }
136 |
137 | [myChildren addObject:parserDelegate];
138 |
139 | [parser setDelegate:parserDelegate];
140 |
141 |
142 | return;
143 | }
144 |
145 | if ([element isEqualToString:@"array"] || [element isEqualToString:@"params"]) {
146 | NSMutableArray *array = [[NSMutableArray alloc] init];
147 |
148 | [self setElementValue:array];
149 |
150 |
151 | [self setElementType:WPXMLRPCElementTypeArray];
152 | } else if ([element isEqualToString:@"struct"]) {
153 | NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
154 |
155 | [self setElementValue:dictionary];
156 |
157 |
158 | [self setElementType:WPXMLRPCElementTypeDictionary];
159 | } else if ([element isEqualToString:@"int"] || [element isEqualToString:@"i4"]) {
160 | [self setElementType:WPXMLRPCElementTypeInteger];
161 | } else if ([element isEqualToString:@"double"]) {
162 | [self setElementType:WPXMLRPCElementTypeDouble];
163 | } else if ([element isEqualToString:@"boolean"]) {
164 | [self setElementType:WPXMLRPCElementTypeBoolean];
165 | } else if ([element isEqualToString:@"string"]) {
166 | [self setElementType:WPXMLRPCElementTypeString];
167 | } else if ([element isEqualToString:@"dateTime.iso8601"]) {
168 | [self setElementType:WPXMLRPCElementTypeDate];
169 | } else if ([element isEqualToString:@"base64"]) {
170 | [self setElementType:WPXMLRPCElementTypeData];
171 | }
172 | }
173 |
174 | - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)element namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName {
175 | if ([element isEqualToString:@"value"] || [element isEqualToString:@"member"] || [element isEqualToString:@"name"]) {
176 | NSString *elementValue = nil;
177 |
178 | if ((myElementType != WPXMLRPCElementTypeArray) && ![self isDictionaryElementType:myElementType]) {
179 | elementValue = [self parseString:myElementValue];
180 |
181 |
182 | myElementValue = nil;
183 | }
184 |
185 | switch (myElementType) {
186 | case WPXMLRPCElementTypeInteger:
187 | myElementValue = [self parseInteger:elementValue];
188 |
189 |
190 | break;
191 | case WPXMLRPCElementTypeDouble:
192 | myElementValue = [self parseDouble:elementValue];
193 |
194 |
195 | break;
196 | case WPXMLRPCElementTypeBoolean:
197 | myElementValue = [self parseBoolean:elementValue];
198 |
199 |
200 | break;
201 | case WPXMLRPCElementTypeString:
202 | case WPXMLRPCElementTypeName:
203 | myElementValue = elementValue;
204 |
205 |
206 | break;
207 | case WPXMLRPCElementTypeDate:
208 | myElementValue = [self parseDate:elementValue];
209 |
210 |
211 | break;
212 | case WPXMLRPCElementTypeData:
213 | myElementValue = [self parseData:elementValue];
214 |
215 |
216 | break;
217 | default:
218 | break;
219 | }
220 |
221 | if (myParent && myElementValue) {
222 | [self addElementValueToParent];
223 | }
224 |
225 | [parser setDelegate:myParent];
226 | }
227 | }
228 |
229 | - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
230 | if ((myElementType == WPXMLRPCElementTypeArray) || [self isDictionaryElementType:myElementType]) {
231 | return;
232 | }
233 |
234 | if (!myElementValue) {
235 | myElementValue = [[NSMutableString alloc] initWithString:string];
236 | } else {
237 | [myElementValue appendString:string];
238 | }
239 | }
240 |
241 | - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
242 | [parser abortParsing];
243 | }
244 |
245 | @end
246 |
247 | #pragma mark -
248 |
249 | @implementation WPXMLRPCDecoderDelegate (WPXMLRPCEventBasedParserDelegatePrivate)
250 |
251 | - (BOOL)isDictionaryElementType:(WPXMLRPCElementType)elementType {
252 | if ((myElementType == WPXMLRPCElementTypeDictionary) || (myElementType == WPXMLRPCElementTypeMember)) {
253 | return YES;
254 | }
255 |
256 | return NO;
257 | }
258 |
259 | #pragma mark -
260 |
261 | - (void)addElementValueToParent {
262 | id parentElementValue = [myParent elementValue];
263 |
264 | switch ([myParent elementType]) {
265 | case WPXMLRPCElementTypeArray:
266 | [parentElementValue addObject:myElementValue];
267 |
268 | break;
269 | case WPXMLRPCElementTypeDictionary:
270 | if ([myElementValue isEqual:[NSNull null]]) {
271 | [parentElementValue removeObjectForKey:myElementKey];
272 | } else {
273 | [parentElementValue setObject:myElementValue forKey:myElementKey];
274 | }
275 |
276 | break;
277 | case WPXMLRPCElementTypeMember:
278 | if (myElementType == WPXMLRPCElementTypeName) {
279 | [myParent setElementKey:myElementValue];
280 | } else {
281 | [myParent setElementValue:myElementValue];
282 | }
283 |
284 | break;
285 | default:
286 | break;
287 | }
288 | }
289 |
290 | #pragma mark -
291 |
292 | - (NSDate *)parseDateString:(NSString *)dateString withFormat:(NSString *)format {
293 | // Set the local to POSIX format so changing date formats on the device doesn't affect the format being passed in
294 | // Since we're assuming GMT the timeZone should be set but this currently breaks WordPress for iOS so leaving for now
295 | NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
296 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
297 | dateFormatter.locale = enUSPOSIXLocale;
298 | // Forcing to GMT time zone to prevent issues with converting from/to time zones
299 | dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];
300 |
301 | NSDate *result = nil;
302 |
303 | [dateFormatter setDateFormat:format];
304 |
305 | result = [dateFormatter dateFromString:dateString];
306 |
307 | return result;
308 | }
309 |
310 | #pragma mark -
311 |
312 | - (NSNumber *)parseInteger:(NSString *)value {
313 | return [NSNumber numberWithLongLong:[value longLongValue]];
314 | }
315 |
316 | - (NSNumber *)parseDouble:(NSString *)value {
317 | return [NSNumber numberWithDouble:[value doubleValue]];
318 | }
319 |
320 | - (NSNumber *)parseBoolean:(NSString *)value {
321 | if ([value isEqualToString:@"1"]) {
322 | return [NSNumber numberWithBool:YES];
323 | }
324 |
325 | return [NSNumber numberWithBool:NO];
326 | }
327 |
328 | - (NSString *)parseString:(NSString *)value {
329 | return [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
330 | }
331 |
332 | - (NSDate *)parseDate:(NSString *)value {
333 | NSDate *result = nil;
334 |
335 | result = [self parseDateString:value withFormat:@"yyyyMMdd'T'HH:mm:ss"];
336 |
337 | if (!result) {
338 | result = [self parseDateString:value withFormat:@"yyyy'-'MM'-'dd'T'HH:mm:ss"];
339 | }
340 |
341 | if (!result) {
342 | result = (NSDate *)[NSNull null];
343 | }
344 |
345 | return result;
346 | }
347 |
348 | - (NSData *)parseData:(NSString *)value {
349 | return [WPBase64Utils decodeString:value];
350 | }
351 |
352 | @end
353 |
--------------------------------------------------------------------------------
/WPXMLRPC/WPXMLRPCEncoder.h:
--------------------------------------------------------------------------------
1 | // WPXMLRPCEncoder.h
2 | //
3 | // Copyright (c) 2013 WordPress - http://wordpress.org/
4 | // Based on Eric Czarny's xmlrpc library - https://github.com/eczarny/xmlrpc
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | @import Foundation;
25 |
26 | NS_ASSUME_NONNULL_BEGIN
27 |
28 | /**
29 | `WPXMLRPCEncoder` encodes a XML-RPC request
30 | */
31 | @interface WPXMLRPCEncoder : NSObject
32 |
33 | - (instancetype)init NS_UNAVAILABLE;
34 |
35 | /**
36 | Initializes a `WPXMLRPCEncoder` object with the specified method and parameters.
37 |
38 | @param method the XML-RPC method for this request
39 | @param parameters an array containing the parameters for the request. If you want to support streaming, you can use either `NSInputStream` or `NSFileHandle` to encode binary data
40 |
41 | @return The newly-initialized XML-RPC request
42 | */
43 | - (instancetype)initWithMethod:(NSString *)method andParameters:(nullable NSArray *)parameters NS_DESIGNATED_INITIALIZER;
44 |
45 | /**
46 | Initializes a `WPXMLRPCEncoder` object with the specified response params.
47 |
48 | @warning The response encoder is for testing purposes only, and hasn't been tested to implement a XML-RPC server
49 |
50 | @param params an array containing the result parameters for the response
51 |
52 | @return The newly-initialized XML-RPC response
53 | */
54 | - (instancetype)initWithResponseParams:(nullable NSArray *)params NS_DESIGNATED_INITIALIZER;
55 |
56 | /**
57 | Initializes a `WPXMLRPCEncoder` object with the specified response fault.
58 |
59 | @warning The response encoder is for testing purposes only, and hasn't been tested to implement a XML-RPC server
60 |
61 | @param faultCode the fault code
62 | @param faultString the fault message string
63 |
64 | @return The newly-initialized XML-RPC response
65 | */
66 | - (instancetype)initWithResponseFaultCode:(NSNumber *)faultCode andString:(NSString *)faultString NS_DESIGNATED_INITIALIZER;
67 |
68 | /**
69 | The XML-RPC method for this request.
70 |
71 | This is a *read-only* property, as requests can't be reused.
72 | */
73 | @property (nonatomic, readonly) NSString * method;
74 |
75 | /**
76 | The XML-RPC parameters for this request.
77 |
78 | This is a *read-only* property, as requests can't be reused.
79 | */
80 | @property (nonatomic, readonly, nullable) NSArray * parameters;
81 |
82 | ///------------------------------------
83 | /// @name Accessing the encoded request
84 | ///------------------------------------
85 |
86 | /**
87 | The encoded request as a `NSData`
88 |
89 | You should pass this to `[NSMutableRequest setHTTPBody:]`
90 | @warning This method is now deprecated you should use dataEncodedWithError:(NSError *)error;
91 |
92 | @return A NSData object with the encoded method and paramaters, nil if there was an error.
93 | */
94 | @property (nonatomic, readonly, nullable) NSData * body DEPRECATED_ATTRIBUTE;
95 |
96 | /**
97 | The encoded request as a `NSData` object.
98 |
99 | You should pass this to `[NSMutableRequest setHTTPBody:]`
100 |
101 | @param error On input, a pointer to an error object. If an error occurs, this pointer is set to an actual error object containing the error information. You may specify nil for this parameter if you do not want the error information.
102 |
103 | @return A NSData object with the encoded method and paramaters, nil if there was an error.
104 | */
105 | - (nullable NSData *) dataEncodedWithError:(NSError *_Nullable*_Nullable) error;
106 |
107 | /**
108 | Encodes the request to the filePath.
109 |
110 | The caller is responsible to manage the resulting file after the request is finished.
111 |
112 | @param error On input, a pointer to an error object. If an error occurs, this pointer is set to an actual error object containing the error information. You may specify nil for this parameter if you do not want the error information.
113 |
114 | @return BOOL, YES if the request was completed with success, NO if some error occurred.
115 | */
116 | - (BOOL)encodeToFile:(NSString *)filePath error:(NSError *_Nullable*_Nullable) error;
117 |
118 | @end
119 |
120 | NS_ASSUME_NONNULL_END
121 |
--------------------------------------------------------------------------------
/WPXMLRPC/WPXMLRPCEncoder.m:
--------------------------------------------------------------------------------
1 | // WPXMLRPCEncoder.m
2 | //
3 | // Copyright (c) 2013 WordPress - http://wordpress.org/
4 | // Based on Eric Czarny's xmlrpc library - https://github.com/eczarny/xmlrpc
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | #import "WPXMLRPCEncoder.h"
25 | #import "WPBase64Utils.h"
26 | #import "WPStringUtils.h"
27 |
28 | #pragma mark -
29 |
30 | @implementation WPXMLRPCEncoder {
31 | NSString *_method;
32 | NSArray *_parameters;
33 | NSFileHandle *_streamingCacheFile;
34 | BOOL _isResponse;
35 | BOOL _isFault;
36 | NSNumber *_faultCode;
37 | NSString *_faultString;
38 | }
39 |
40 | - (instancetype)initWithMethod:(NSString *)method andParameters:(NSArray *)parameters {
41 | self = [super init];
42 | if (self) {
43 | _method = method;
44 | _parameters = parameters;
45 | }
46 |
47 | return self;
48 | }
49 |
50 | - (instancetype)initWithResponseParams:(NSArray *)params {
51 | self = [super init];
52 | if (self) {
53 | _parameters = params;
54 | _isResponse = YES;
55 | }
56 | return self;
57 | }
58 |
59 | - (instancetype)initWithResponseFaultCode:(NSNumber *)faultCode andString:(NSString *)faultString {
60 | self = [super init];
61 | if (self) {
62 | _faultCode = faultCode;
63 | _faultString = faultString;
64 | _isResponse = YES;
65 | _isFault = YES;
66 | }
67 | return self;
68 | }
69 |
70 |
71 | #pragma mark - Public methods
72 |
73 | - (NSData *)body {
74 | return [self dataEncodedWithError:nil];
75 | }
76 |
77 | - (NSData *)dataEncodedWithError:(NSError **) error {
78 | NSString * filePath = [self tmpFilePathForCache];
79 | if (![self encodeToFile:filePath error:error]){
80 | return nil;
81 | }
82 |
83 | NSData * encodedData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingUncached error:error];
84 |
85 | [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
86 |
87 | return encodedData;
88 | }
89 |
90 | - (BOOL)encodeToFile:(NSString *)filePath error:(NSError **) error {
91 | NSFileManager *fileManager = [NSFileManager defaultManager];
92 | [fileManager createFileAtPath:filePath contents:nil attributes:nil];
93 | _streamingCacheFile = [NSFileHandle fileHandleForWritingToURL:[NSURL fileURLWithPath:filePath] error:error];
94 | if (!_streamingCacheFile){
95 | return NO;
96 | }
97 |
98 | [self encodeForStreaming];
99 |
100 | return YES;
101 | }
102 |
103 | #pragma mark - Private methods
104 |
105 | - (void)encodeForStreaming {
106 | [self appendString:@""];
107 | if (_isResponse) {
108 | [self appendString:@""];
109 | if (_isFault) {
110 | [self appendString:@""];
111 | [self encodeDictionary:@{@"faultCode": _faultCode, @"faultString": _faultString}];
112 | [self appendString:@" "];
113 | } else {
114 | [self appendString:@""];
115 | }
116 | } else {
117 | [self appendString:@""];
118 | [self encodeString:_method omitTag:YES];
119 | [self appendString:@" "];
120 | }
121 |
122 | if (_parameters) {
123 | NSEnumerator *enumerator = [_parameters objectEnumerator];
124 | id parameter = nil;
125 |
126 | while ((parameter = [enumerator nextObject])) {
127 | [self appendString:@" "];
128 | [self encodeObject:parameter];
129 | [self appendString:@""];
130 | }
131 | }
132 |
133 | if (_isResponse) {
134 | if (!_isFault) {
135 | [self appendString:@" "];
136 | }
137 | [self appendString:@" "];
138 | } else {
139 | [self appendString:@""];
140 | [self appendString:@""];
141 | }
142 |
143 | [_streamingCacheFile synchronizeFile];
144 | }
145 |
146 | - (void)valueTag:(NSString *)tag value:(NSString *)value {
147 | [self appendFormat:@"<%@>%@%@> ", tag, value, tag];
148 | }
149 |
150 | - (void)encodeObject:(id)object {
151 | if (!object) {
152 | return;
153 | }
154 |
155 | if ([object isKindOfClass:[NSArray class]]) {
156 | [self encodeArray:object];
157 | } else if ([object isKindOfClass:[NSDictionary class]]) {
158 | [self encodeDictionary:object];
159 | } else if (((__bridge CFBooleanRef)object == kCFBooleanTrue) || ((__bridge CFBooleanRef)object == kCFBooleanFalse)) {
160 | [self encodeBoolean:(CFBooleanRef)object];
161 | } else if ([object isKindOfClass:[NSNumber class]]) {
162 | [self encodeNumber:object];
163 | } else if ([object isKindOfClass:[NSString class]]) {
164 | [self encodeString:object omitTag:NO];
165 | } else if ([object isKindOfClass:[NSDate class]]) {
166 | [self encodeDate:object];
167 | } else if ([object isKindOfClass:[NSData class]]) {
168 | [self encodeData:object];
169 | } else if ([object isKindOfClass:[NSInputStream class]]) {
170 | [self encodeInputStream:object];
171 | } else if ([object isKindOfClass:[NSFileHandle class]]) {
172 | [self encodeFileHandle:object];
173 | } else {
174 | [self encodeString:object omitTag:NO];
175 | }
176 | }
177 |
178 | - (void)encodeArray:(NSArray *)array {
179 | NSEnumerator *enumerator = [array objectEnumerator];
180 |
181 | [self appendString:@""];
182 |
183 | id object = nil;
184 |
185 | while (object = [enumerator nextObject]) {
186 | [self encodeObject:object];
187 | }
188 |
189 | [self appendString:@" "];
190 | }
191 |
192 | - (void)encodeDictionary:(NSDictionary *)dictionary {
193 | NSEnumerator *enumerator = [dictionary keyEnumerator];
194 |
195 | [self appendString:@""];
196 |
197 | NSString *key = nil;
198 |
199 | while (key = [enumerator nextObject]) {
200 | [self appendString:@""];
201 | [self appendString:@""];
202 | [self encodeString:key omitTag:YES];
203 | [self appendString:@" "];
204 | [self encodeObject:[dictionary objectForKey:key]];
205 | [self appendString:@" "];
206 | }
207 |
208 | [self appendString:@" "];
209 | }
210 |
211 | - (void)encodeBoolean:(CFBooleanRef)boolean {
212 | if (boolean == kCFBooleanTrue) {
213 | [self valueTag:@"boolean" value:@"1"];
214 | } else {
215 | [self valueTag:@"boolean" value:@"0"];
216 | }
217 | }
218 |
219 | - (void)encodeNumber:(NSNumber *)number {
220 | NSString *numberType = [NSString stringWithCString:[number objCType] encoding:NSUTF8StringEncoding];
221 |
222 | if ([numberType isEqualToString:@"d"]) {
223 | [self valueTag:@"double" value:[number stringValue]];
224 | } else {
225 | [self valueTag:@"i4" value:[number stringValue]];
226 | }
227 | }
228 |
229 | - (void)encodeString:(NSString *)string omitTag:(BOOL)omitTag {
230 | if (omitTag)
231 | [self appendString:[WPStringUtils escapedStringWithString:string]];
232 | else
233 | [self valueTag:@"string" value:[WPStringUtils escapedStringWithString:string]];
234 | }
235 |
236 | - (void)encodeDate:(NSDate *)date {
237 | unsigned components = kCFCalendarUnitYear | kCFCalendarUnitMonth | kCFCalendarUnitDay | kCFCalendarUnitHour | kCFCalendarUnitMinute | kCFCalendarUnitSecond;
238 | NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
239 | [calendar setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
240 | NSDateComponents *dateComponents = [calendar components:components fromDate:date];
241 | NSString *buffer = [NSString stringWithFormat:@"%.4ld%.2d%.2dT%.2d:%.2ld:%.2ld%@", (long)[dateComponents year], (int)[dateComponents month], (int)[dateComponents day], (int)[dateComponents hour], (long)[dateComponents minute], (long)[dateComponents second], @"Z", nil];
242 |
243 | [self valueTag:@"dateTime.iso8601" value:buffer];
244 | }
245 |
246 | - (void)encodeData:(NSData *)data {
247 | [self valueTag:@"base64" value:[WPBase64Utils encodeData:data]];
248 | }
249 |
250 | - (void)encodeInputStream:(NSInputStream *)stream {
251 | [self appendString:@""];
252 |
253 | [WPBase64Utils encodeInputStream:stream withChunkHandler:^(NSString *chunk) {
254 | [self appendString:chunk];
255 | }];
256 |
257 | [self appendString:@" "];
258 | }
259 |
260 | - (void)encodeFileHandle:(NSFileHandle *)handle {
261 | [self appendString:@""];
262 |
263 | [WPBase64Utils encodeFileHandle:handle withChunkHandler:^(NSString *chunk) {
264 | [self appendString:chunk];
265 | }];
266 |
267 | [self appendString:@" "];
268 | }
269 |
270 | - (void)appendString:(NSString *)aString {
271 | [_streamingCacheFile writeData:[aString dataUsingEncoding:NSUTF8StringEncoding]];
272 | }
273 |
274 | - (void)appendFormat:(NSString *)format, ... {
275 | va_list ap;
276 | va_start(ap, format);
277 | NSString *message = [[NSString alloc] initWithFormat:format arguments:ap];
278 |
279 | [self appendString:message];
280 | }
281 |
282 | - (NSString *)tmpFilePathForCache {
283 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
284 | NSString *directory = [paths objectAtIndex:0];
285 | NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
286 | NSString * tmpPath = [directory stringByAppendingPathComponent:guid];
287 | return tmpPath;
288 | }
289 |
290 | @end
291 |
--------------------------------------------------------------------------------
/WPXMLRPC/include/WPXMLRPC.h:
--------------------------------------------------------------------------------
1 | ../WPXMLRPC.h
--------------------------------------------------------------------------------
/WPXMLRPC/include/WPXMLRPCDecoder.h:
--------------------------------------------------------------------------------
1 | ../WPXMLRPCDecoder.h
--------------------------------------------------------------------------------
/WPXMLRPC/include/WPXMLRPCEncoder.h:
--------------------------------------------------------------------------------
1 | ../WPXMLRPCEncoder.h
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/AlternativeDateFormatsTestCase.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 20091201T20:49:00
9 | 2009-12-01T20:50:00
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/DefaultTypeTestCase.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hello World!
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/EmptyBooleanTestCase.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/EmptyDataTestCase.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/EmptyDoubleTestCase.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/EmptyIntegerTestCase.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/EmptyStringTestCase.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/EscapingTestCase.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Are < & > escaped correctly?
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/IncompleteXmlTest.xml:
--------------------------------------------------------------------------------
1 | PHP Notice: Some garbage before preamble
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Hello World!
10 | 42
11 | 3.14
12 | 1
13 | 20090718T21:34:00
14 | eW91IGNhbid0IHJlYWQgdGhpcyE=
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/InvalidFault.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/NoXmlResponseTestCase.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Why Duda, why?
11 |
12 |
13 |
14 | Don't mess with my XML-RPC bro!
15 |
16 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/OverflowInteger32TestCase.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 9223372036854775807
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/RequestTestCase.xml:
--------------------------------------------------------------------------------
1 | wp.getUsersBlogs username password
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/SimpleArrayTestCase.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Hello World!
9 | 42
10 | 3.14
11 | 1
12 | 20090718T21:34:00
13 | eW91IGNhbid0IHJlYWQgdGhpcyE=
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/SimpleStructTestCase.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Name
9 | Eric Czarny
10 |
11 |
12 | Birthday
13 | 1984-04-15T00:00:00
14 |
15 |
16 | Age
17 | 25
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/TestCases.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | EscapingTestCase
6 | Are < & > escaped correctly?
7 | AlternativeDateFormatsTestCase
8 |
9 | 2009-12-01T20:49:00Z
10 | 2009-12-01T20:50:00Z
11 |
12 | DefaultTypeTestCase
13 | Hello World!
14 | EmptyBooleanTestCase
15 | 0
16 | EmptyDataTestCase
17 |
18 | EmptyDoubleTestCase
19 | 0
20 | EmptyIntegerTestCase
21 | 0
22 | EmptyStringTestCase
23 |
24 | SimpleArrayTestCase
25 |
26 | Hello World!
27 | 42
28 | 3.14
29 | 1
30 | 2009-07-18T21:34:00Z
31 | eW91IGNhbid0IHJlYWQgdGhpcyE=
32 |
33 | RequestTestCase
34 |
35 | methodName
36 | wp.getUsersBlogs
37 | params
38 |
39 | username
40 | password
41 |
42 |
43 | SimpleStructTestCase
44 |
45 | Name
46 | Eric Czarny
47 | Birthday
48 | 1984-04-15T00:00:00Z
49 | Age
50 | 25
51 |
52 | IncompleteXmlTest
53 |
54 | Hello World!
55 | 42
56 | 3.14
57 | 1
58 | 2009-07-18T21:34:00Z
59 | eW91IGNhbid0IHJlYWQgdGhpcyE=
60 |
61 | OverflowInteger32TestCase
62 | 9223372036854775807
63 |
64 |
65 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/TestImage.base64:
--------------------------------------------------------------------------------
1 | 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
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/TestImage.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wordpress-mobile/wpxmlrpc/810695e0455396cfc5fad460f16f64eefd8b5087/WPXMLRPCTest/Test Data/TestImage.bin
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/entities.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | dateCreated 20120126T20:38:14
7 | userid 1
8 | postid 37
9 | description <h2>Reserved Characters in HTML</h2>
10 | Some characters are reserved in HTML and XHTML. For example, you cannot use
11 | the greater than or less than signs within your text because the browser could
12 | mistake them for markup.
13 |
14 | HTML and XHTML processors must support the five special characters listed in
15 | the table below:
16 | <table class="reference">
17 | <tbody>
18 | <tr>
19 | <th align="left">Character</th>
20 | <th align="left">Entity Number</th>
21 | <th align="left">Entity Name</th>
22 | <th align="left">Description</th>
23 | </tr>
24 | <tr>
25 | <td>"</td>
26 | <td>&#34;</td>
27 | <td>&quot;</td>
28 | <td>quotation mark</td>
29 | </tr>
30 | <tr>
31 | <td>'</td>
32 | <td>&#39;</td>
33 | <td>&apos;</td>
34 | <td>apostrophe</td>
35 | </tr>
36 | <tr>
37 | <td>&</td>
38 | <td>&#38;</td>
39 | <td>&amp;</td>
40 | <td>ampersand</td>
41 | </tr>
42 | <tr>
43 | <td><</td>
44 | <td>&#60;</td>
45 | <td>&lt;</td>
46 | <td>less-than</td>
47 | </tr>
48 | <tr>
49 | <td>></td>
50 | <td>&#62;</td>
51 | <td>&gt;</td>
52 | <td>greater-than</td>
53 | </tr>
54 | </tbody>
55 | </table>
56 | title Entities everywhere
57 | link http://xmlfixtestcom.powweb.com/blog/2012/01/26/entities-everywhere/
58 | permaLink http://xmlfixtestcom.powweb.com/blog/2012/01/26/entities-everywhere/
59 | categories
60 | Uncategorized
61 |
62 | mt_excerpt
63 | mt_text_more
64 | mt_allow_comments 1
65 | mt_allow_pings 1
66 | mt_keywords
67 | wp_slug entities-everywhere
68 | wp_password
69 | wp_author_id 1
70 | wp_author_display_name q
71 | date_created_gmt 20120126T20:38:14
72 | post_status publish
73 | custom_fields
74 |
75 | wp_post_format standard
76 | sticky 0
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Test Data/entitiesDecoded.xml:
--------------------------------------------------------------------------------
1 | Reserved Characters in HTML
2 | Some characters are reserved in HTML and XHTML. For example, you cannot use
3 | the greater than or less than signs within your text because the browser could
4 | mistake them for markup.
5 |
6 | HTML and XHTML processors must support the five special characters listed in
7 | the table below:
8 |
9 |
10 |
11 | Character
12 | Entity Number
13 | Entity Name
14 | Description
15 |
16 |
17 | "
18 | "
19 | "
20 | quotation mark
21 |
22 |
23 | '
24 | '
25 | '
26 | apostrophe
27 |
28 |
29 | &
30 | &
31 | &
32 | ampersand
33 |
34 |
35 | <
36 | <
37 | <
38 | less-than
39 |
40 |
41 | >
42 | >
43 | >
44 | greater-than
45 |
46 |
47 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Tests/Helper.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface NSBundle (Helper)
4 |
5 | + (NSBundle *)wpxmlrpc_assetsBundle;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Tests/Helper.m:
--------------------------------------------------------------------------------
1 | #import "Helper.h"
2 |
3 | #if !defined(SWIFT_PACKAGE)
4 | @interface WPXMLRPCAssetsBundleFinder: NSObject
5 | @end
6 |
7 | @implementation WPXMLRPCAssetsBundleFinder
8 | @end
9 | #endif
10 |
11 | @implementation NSBundle (Helper)
12 |
13 | + (NSBundle *)wpxmlrpc_assetsBundle {
14 | #if SWIFT_PACKAGE
15 | return SWIFTPM_MODULE_BUNDLE;
16 | #else
17 | return [NSBundle bundleForClass:[WPXMLRPCAssetsBundleFinder class]];
18 | #endif
19 | }
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Tests/WPBase64UtilsTest.m:
--------------------------------------------------------------------------------
1 | #import "WPBase64Utils.h"
2 | #import "Helper.h"
3 |
4 | #import
5 |
6 | @interface WPBase64UtilsTest : XCTestCase
7 |
8 | @end
9 |
10 | @implementation WPBase64UtilsTest {
11 | NSString *expectedEncoded;
12 | NSData *expectedDecoded;
13 | NSData *expectedDecodedControlCharacters;
14 | NSString *encodedFilePath;
15 | NSString *encodedControlCharacters;
16 | }
17 |
18 | - (void)setUp {
19 | expectedEncoded = [NSString stringWithContentsOfFile:[[NSBundle wpxmlrpc_assetsBundle] pathForResource:@"TestImage" ofType:@"base64"] encoding:NSASCIIStringEncoding error:nil];
20 | encodedFilePath = [[NSBundle wpxmlrpc_assetsBundle] pathForResource:@"TestImage" ofType:@"bin"];
21 | expectedDecoded = [NSData dataWithContentsOfFile:encodedFilePath];
22 | XCTAssertEqual(expectedDecoded.length, 4326);
23 |
24 | encodedControlCharacters = @"\r\n";
25 | expectedDecodedControlCharacters = [@"" dataUsingEncoding:NSASCIIStringEncoding];
26 | }
27 |
28 | - (void)testEncodeWithData {
29 | NSString *parsedEncoded = [WPBase64Utils encodeData:expectedDecoded];
30 | XCTAssertEqualObjects(expectedEncoded, parsedEncoded);
31 | }
32 |
33 | - (void)testDecodeWithData {
34 | NSData *parsedDecoded = [WPBase64Utils decodeString:expectedEncoded];
35 | XCTAssertEqualObjects(expectedDecoded, parsedDecoded);
36 | }
37 |
38 | - (void)testDecodeControlCharactersString {
39 | NSData *parsedDecoded = [WPBase64Utils decodeString:encodedControlCharacters];
40 | XCTAssertEqualObjects(expectedDecodedControlCharacters, parsedDecoded);
41 | }
42 |
43 | - (void)testEncodeWithInputStream {
44 | NSMutableString *parsedEncoded = [NSMutableString string];
45 | [WPBase64Utils encodeInputStream:[NSInputStream inputStreamWithFileAtPath:encodedFilePath] withChunkHandler:^(NSString *chunk) {
46 | [parsedEncoded appendString:chunk];
47 | }];
48 | XCTAssertEqualObjects(expectedEncoded, parsedEncoded);
49 | }
50 |
51 | - (void)testEncodeWithFileHandle {
52 | NSMutableString *parsedEncoded = [NSMutableString string];
53 | [WPBase64Utils encodeFileHandle:[NSFileHandle fileHandleForReadingAtPath:encodedFilePath] withChunkHandler:^(NSString *chunk) {
54 | [parsedEncoded appendString:chunk];
55 | }];
56 | XCTAssertEqualObjects(expectedEncoded, parsedEncoded);
57 | }
58 |
59 | @end
60 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Tests/WPXMLRPCDataCleanerTest.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import "WPXMLRPCDataCleaner.h"
3 |
4 | static NSString * const CompleteResponse = @"Hello! ";
5 | static NSString * const CompleteFault = @"faultCode 403 faultString Incorrect username or password. ";
6 |
7 | @interface WPXMLRPCDataCleaner (CleaningSteps)
8 | - (NSString *)cleanClosingTagIfNeeded:(NSString *)str lengthOfCharactersPrecedingPreamble:(NSInteger)length;
9 | @end
10 |
11 | @interface WPXMLRPCDataCleanerTest : XCTestCase
12 | @end
13 |
14 | @implementation WPXMLRPCDataCleanerTest
15 |
16 |
17 |
18 | - (void)testClosingTagRepaired
19 | {
20 | WPXMLRPCDataCleaner *cleaner = [[WPXMLRPCDataCleaner alloc] init];
21 |
22 | // Test a whole response
23 | NSString *cleanedString = [cleaner cleanClosingTagIfNeeded:CompleteResponse lengthOfCharactersPrecedingPreamble:0];
24 | XCTAssertEqualObjects(CompleteResponse, cleanedString, @"A good reponse should just be returned intact.");
25 | }
26 |
27 | - (void)testReturnsGoodStringWhenPreambleWasCleanedOfJunk
28 | {
29 | WPXMLRPCDataCleaner *cleaner = [[WPXMLRPCDataCleaner alloc] init];
30 | // Test a whole response but some info was cleaned from before the preamble
31 | NSString *cleanedString = [cleaner cleanClosingTagIfNeeded:CompleteResponse lengthOfCharactersPrecedingPreamble:35];
32 | XCTAssertEqualObjects(CompleteResponse, cleanedString, @"Info cleaned from the preamble should not affect a good response.");
33 | }
34 |
35 | - (void)testReturnsOriginalStringWhenPreambleHadTooMuchJunk
36 | {
37 | WPXMLRPCDataCleaner *cleaner = [[WPXMLRPCDataCleaner alloc] init];
38 | // Test a truncated response where too much info was cleaned from before the preamble.
39 | NSString *truncatedResponse = [CompleteResponse substringWithRange:NSMakeRange(0, [CompleteResponse length] - 35)];
40 | NSString *cleanedString = [cleaner cleanClosingTagIfNeeded:truncatedResponse lengthOfCharactersPrecedingPreamble:35];
41 | XCTAssertEqualObjects(truncatedResponse, cleanedString, @"Should not try to fix a response where too much junk was removed from the preamble.");
42 | }
43 |
44 | - (void)testCleansResponseTurncatedBeforeParam
45 | {
46 | WPXMLRPCDataCleaner *cleaner = [[WPXMLRPCDataCleaner alloc] init];
47 | // Test a response truncated just before the param tag
48 | NSString *truncatedResponse = [CompleteResponse substringWithRange:NSMakeRange(0, [CompleteResponse length] - 34)];
49 | NSString *cleanedString = [cleaner cleanClosingTagIfNeeded:truncatedResponse lengthOfCharactersPrecedingPreamble:0];
50 | XCTAssertEqualObjects(CompleteResponse, cleanedString, @"Failed to repair a damanged closing param tag.");
51 | }
52 |
53 | - (void)testCleansResponseTurncatedInParam
54 | {
55 | WPXMLRPCDataCleaner *cleaner = [[WPXMLRPCDataCleaner alloc] init];
56 | // Test a response truncated in the param tag
57 | NSString *truncatedResponse = [CompleteResponse substringWithRange:NSMakeRange(0, [CompleteResponse length] - 33)];
58 | NSString *cleanedString = [cleaner cleanClosingTagIfNeeded:truncatedResponse lengthOfCharactersPrecedingPreamble:0];
59 | XCTAssertEqualObjects(CompleteResponse, cleanedString, @"Failed to repair a damanged closing param tag.");
60 | }
61 |
62 | - (void)testCleansResponseTurncatedInParams
63 | {
64 | WPXMLRPCDataCleaner *cleaner = [[WPXMLRPCDataCleaner alloc] init];
65 | // test a response truncated in the params tag
66 | NSString *truncatedResponse = [CompleteResponse substringWithRange:NSMakeRange(0, [CompleteResponse length] - 20)];
67 | NSString *cleanedString = [cleaner cleanClosingTagIfNeeded:truncatedResponse lengthOfCharactersPrecedingPreamble:0];
68 | XCTAssertEqualObjects(CompleteResponse, cleanedString, @"Failed to repair a damanged closing params tag.");
69 | }
70 |
71 | - (void)testCleansResponseTurncatedInMethodResponse
72 | {
73 | WPXMLRPCDataCleaner *cleaner = [[WPXMLRPCDataCleaner alloc] init];
74 | // test a response truncated in the methodResponse tag
75 | NSString *truncatedResponse = [CompleteResponse substringWithRange:NSMakeRange(0, [CompleteResponse length] - 10)];
76 | NSString *cleanedString = [cleaner cleanClosingTagIfNeeded:truncatedResponse lengthOfCharactersPrecedingPreamble:0];
77 | XCTAssertEqualObjects(CompleteResponse, cleanedString, @"Failed to repair a damanged closing methodResponse tag.");
78 | }
79 |
80 | - (void)testCleansFaultTurncateBeforeValue
81 | {
82 | WPXMLRPCDataCleaner *cleaner = [[WPXMLRPCDataCleaner alloc] init];
83 | // Test a fault truncated at the value tag
84 | NSString *truncatedFault = [CompleteFault substringWithRange:NSMakeRange(0, [CompleteFault length] - 33)];
85 | NSString *cleanedString = [cleaner cleanClosingTagIfNeeded:truncatedFault lengthOfCharactersPrecedingPreamble:0];
86 | XCTAssertEqualObjects(CompleteFault, cleanedString, @"Failed to repair a damanged closing value tag.");
87 | }
88 |
89 | - (void)testCleansFaultTurncatedIValue
90 | {
91 | WPXMLRPCDataCleaner *cleaner = [[WPXMLRPCDataCleaner alloc] init];
92 | // Test a fault truncated in the value tag
93 | NSString *truncatedFault = [CompleteFault substringWithRange:NSMakeRange(0, [CompleteFault length] - 30)];
94 | NSString *cleanedString = [cleaner cleanClosingTagIfNeeded:truncatedFault lengthOfCharactersPrecedingPreamble:0];
95 | XCTAssertEqualObjects(CompleteFault, cleanedString, @"Failed to repair a damanged closing value tag.");
96 | }
97 |
98 | - (void)testCleansFaultTurncatedInFault
99 | {
100 | WPXMLRPCDataCleaner *cleaner = [[WPXMLRPCDataCleaner alloc] init];
101 | // Test a fault truncated in the fault tag
102 | NSString *truncatedFault = [CompleteFault substringWithRange:NSMakeRange(0, [CompleteFault length] - 20)];
103 | NSString *cleanedString = [cleaner cleanClosingTagIfNeeded:truncatedFault lengthOfCharactersPrecedingPreamble:0];
104 | XCTAssertEqualObjects(CompleteFault, cleanedString, @"Failed to repair a damanged closing fault tag.");
105 | }
106 |
107 | - (void)testCleansFaultTurncatedInMethodResponse
108 | {
109 | WPXMLRPCDataCleaner *cleaner = [[WPXMLRPCDataCleaner alloc] init];
110 | // Test a fault truncated in the method response tag
111 | NSString *truncatedFault = [CompleteFault substringWithRange:NSMakeRange(0, [CompleteFault length] - 10)];
112 | NSString *cleanedString = [cleaner cleanClosingTagIfNeeded:truncatedFault lengthOfCharactersPrecedingPreamble:0];
113 | XCTAssertEqualObjects(CompleteFault, cleanedString, @"Failed to repair a damanged closing methodResponse tag.");
114 | }
115 |
116 | @end
117 |
118 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Tests/WPXMLRPCDecoderTest.m:
--------------------------------------------------------------------------------
1 | #import "WPXMLRPCDecoder.h"
2 | #import "WPStringUtils.h"
3 | #import "Helper.h"
4 | #import
5 |
6 | @interface WPXMLRPCDecoderTest : XCTestCase
7 | @end
8 |
9 | @implementation WPXMLRPCDecoderTest {
10 | NSDictionary *myTestCases;
11 | }
12 |
13 | - (void)setUp {
14 | myTestCases = [self testCases];
15 | XCTAssertNotNil(myTestCases);
16 | [NSTimeZone setDefaultTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
17 | }
18 |
19 | - (void)testEventBasedParser {
20 | NSEnumerator *testCaseEnumerator = [myTestCases keyEnumerator];
21 | id testCaseName;
22 |
23 | while (testCaseName = [testCaseEnumerator nextObject]) {
24 | if ([testCaseName isEqualToString:@"IncompleteXmlTest"]) {
25 | continue;
26 | }
27 | NSString *testCase = [[NSBundle wpxmlrpc_assetsBundle] pathForResource:testCaseName ofType:@"xml"];
28 | NSData *testCaseData =[[NSData alloc] initWithContentsOfFile:testCase];
29 | WPXMLRPCDecoder *decoder = [[WPXMLRPCDecoder alloc] initWithData:testCaseData];
30 | id testCaseResult = [myTestCases objectForKey:testCaseName];
31 | id parsedResult = [decoder object];
32 |
33 | XCTAssertEqualObjects(parsedResult, testCaseResult);
34 | }
35 | }
36 |
37 | - (void)testCTidyDataCleaner {
38 | // Only run if CTidy is available
39 | if (!NSClassFromString(@"CTidy")) {
40 | return;
41 | }
42 | NSString *testCaseName = @"IncompleteXmlTest";
43 | NSString *testCase = [[NSBundle wpxmlrpc_assetsBundle] pathForResource:testCaseName ofType:@"xml"];
44 | NSData *testCaseData =[[NSData alloc] initWithContentsOfFile:testCase];
45 | WPXMLRPCDecoder *decoder = [[WPXMLRPCDecoder alloc] initWithData:testCaseData];
46 | id testCaseResult = [myTestCases objectForKey:testCaseName];
47 | id parsedResult = [decoder object];
48 |
49 | XCTAssertEqualObjects(parsedResult, testCaseResult);
50 | }
51 |
52 | - (void)testNoXmlThrowsError {
53 | NSString *testCase = [[NSBundle wpxmlrpc_assetsBundle] pathForResource:@"NoXmlResponseTestCase" ofType:@"xml"];
54 | NSData *testCaseData =[[NSData alloc] initWithContentsOfFile:testCase];
55 | WPXMLRPCDecoder *decoder = [[WPXMLRPCDecoder alloc] initWithData:testCaseData];
56 | XCTAssertNil([decoder object]);
57 | XCTAssertNotNil([decoder error]);
58 | NSError *error = [decoder error];
59 | XCTAssertEqualObjects([error domain], WPXMLRPCErrorDomain);
60 | XCTAssertEqual([error code], WPXMLRPCInvalidInputError);
61 | }
62 |
63 | - (void)testInvalidFaultXmlThrowsError {
64 | NSString *testCase = [[NSBundle wpxmlrpc_assetsBundle] pathForResource:@"InvalidFault" ofType:@"xml"];
65 | NSData *testCaseData =[[NSData alloc] initWithContentsOfFile:testCase];
66 | WPXMLRPCDecoder *decoder = [[WPXMLRPCDecoder alloc] initWithData:testCaseData];
67 | XCTAssertNotNil([decoder object]);
68 | XCTAssertNotNil([decoder error]);
69 | NSError *error = [decoder error];
70 | XCTAssertEqualObjects([error domain], WPXMLRPCErrorDomain);
71 | XCTAssertEqual([error code], WPXMLRPCInvalidInputError);
72 | }
73 |
74 | #pragma mark -
75 |
76 | - (NSDictionary *)testCases {
77 | NSString *file = [[NSBundle wpxmlrpc_assetsBundle] pathForResource:@"TestCases" ofType:@"plist"];
78 | NSDictionary *testCases = [[NSDictionary alloc] initWithContentsOfFile:file];
79 |
80 | return testCases;
81 | }
82 |
83 | @end
84 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Tests/WPXMLRPCEncoderTest.m:
--------------------------------------------------------------------------------
1 | #import "WPXMLRPCEncoder.h"
2 | #import "Helper.h"
3 | #import
4 |
5 | @interface WPXMLRPCEncoderTest : XCTestCase
6 |
7 | @end
8 |
9 | @implementation WPXMLRPCEncoderTest
10 |
11 | - (void)testRequestEncoder {
12 | WPXMLRPCEncoder *encoder = [[WPXMLRPCEncoder alloc] initWithMethod:@"wp.getUsersBlogs" andParameters:@[@"username", @"password"]];
13 | NSString *testCase = [[NSBundle wpxmlrpc_assetsBundle] pathForResource:@"RequestTestCase" ofType:@"xml"];
14 | NSString *testCaseData = [[NSString alloc] initWithContentsOfFile:testCase encoding:NSUTF8StringEncoding error:nil];
15 | NSString *parsedResult = [[NSString alloc] initWithData:[encoder dataEncodedWithError:nil] encoding:NSUTF8StringEncoding];
16 | XCTAssertEqualObjects(parsedResult, testCaseData);
17 | }
18 |
19 | /*
20 | This is meant to test https://github.com/wordpress-mobile/wpxmlrpc/issues/15
21 |
22 | I haven't found a way to change the locale for testing, so I had to switch the calendar manually on the simulator settings
23 | */
24 | - (void)testDateEncoder {
25 | WPXMLRPCEncoder *encoder = [[WPXMLRPCEncoder alloc] initWithMethod:@"wp.getUsersBlogs" andParameters:@[[NSDate dateWithTimeIntervalSince1970:0]]];
26 | NSString *result = [[NSString alloc] initWithData:[encoder dataEncodedWithError:nil] encoding:NSUTF8StringEncoding];
27 | NSString *expected = @"wp.getUsersBlogs 19700101T00:00:00Z ";
28 | XCTAssertEqualObjects(expected, result);
29 | }
30 |
31 | - (void)testStreamingEncoder {
32 | NSString * filePath = [[NSBundle wpxmlrpc_assetsBundle] pathForResource:@"TestImage" ofType:@"bin"];
33 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
34 | NSString *directory = [paths objectAtIndex:0];
35 | NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
36 | NSString *cacheFilePath = [directory stringByAppendingPathComponent:guid];
37 | WPXMLRPCEncoder *encoder = [[WPXMLRPCEncoder alloc] initWithMethod:@"wp.getUsersBlogs" andParameters:@[@"username", @"password", @{@"bits": [NSInputStream inputStreamWithFileAtPath:filePath]}]];
38 |
39 | NSError * error = nil;
40 |
41 | [encoder encodeToFile:cacheFilePath error:&error];
42 | encoder = nil;
43 |
44 | XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheFilePath isDirectory:nil],@"Cache File must be present");
45 |
46 | XCTAssertTrue([[NSFileManager defaultManager] removeItemAtPath:cacheFilePath error:nil], @"It must be possible to remove the file");
47 |
48 | }
49 |
50 | @end
51 |
--------------------------------------------------------------------------------
/WPXMLRPCTest/Tests/WPXMLRPCEntitiesTest.m:
--------------------------------------------------------------------------------
1 | #import "WPXMLRPCDecoder.h"
2 | #import "WPXMLRPCEncoder.h"
3 | #import "WPStringUtils.h"
4 | #import "Helper.h"
5 |
6 | #import
7 |
8 | @interface WPXMLRPCEntitiesTest : XCTestCase
9 |
10 | @end
11 |
12 | @implementation WPXMLRPCEntitiesTest
13 |
14 | - (void)testXMLEntitiesDecoding {
15 | NSString *testCase = [[NSBundle wpxmlrpc_assetsBundle] pathForResource:@"entities" ofType:@"xml"];
16 | NSData *testCaseData =[[NSData alloc] initWithContentsOfFile:testCase];
17 | WPXMLRPCDecoder *decoder = [[WPXMLRPCDecoder alloc] initWithData:testCaseData];
18 | NSString *decoded = [[decoder object] objectForKey:@"description"];
19 |
20 | NSString *expectedPath = [[NSBundle wpxmlrpc_assetsBundle] pathForResource:@"entitiesDecoded" ofType:@"xml"];
21 | NSData *expectedData =[[NSData alloc] initWithContentsOfFile:expectedPath];
22 | NSString *expected = [[NSString alloc] initWithData:expectedData encoding:NSUTF8StringEncoding];
23 | XCTAssertEqualObjects(decoded, expected);
24 |
25 | decoded = [WPStringUtils unescapedStringWithString:@"<td>></td><td>&#62;</td><td>&gt;</td>"];
26 | expected = @"> > > ";
27 | XCTAssertEqualObjects(decoded, expected);
28 | }
29 |
30 | - (void)testXmlEntitiesEncoding {
31 | WPXMLRPCEncoder *encoder = [[WPXMLRPCEncoder alloc] initWithMethod:@"fake.test" andParameters:@[@"<b> tag & "other" \"tags\""]];
32 | NSString *encoded = [[NSString alloc] initWithData:[encoder dataEncodedWithError:nil] encoding:NSUTF8StringEncoding];
33 | NSString *expected = @"fake.test <b><b></b> tag & "other" \"tags\" ";
34 | XCTAssertEqualObjects(encoded, expected);
35 | }
36 |
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/fastlane/Fastfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | default_platform(:ios)
4 |
5 | platform :ios do
6 | desc 'Builds the project and runs tests'
7 | lane :test do
8 | run_tests(
9 | package_path: '.',
10 | scheme: 'wpxmlrpc',
11 | device: 'iPhone 14',
12 | prelaunch_simulator: true,
13 | buildlog_path: File.join(__dir__, '.build', 'logs'),
14 | derived_data_path: File.join(__dir__, '.build', 'derived-data')
15 | )
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/wpxmlrpc.podspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | Pod::Spec.new do |s|
4 | s.name = 'wpxmlrpc'
5 | s.version = '0.10.0'
6 |
7 | s.summary = 'Lightweight XML-RPC library.'
8 | s.description = <<-DESC
9 | This framework contains a very lightweight XML-RPC library, allowing you to encode and decode XML-RPC request payloads.
10 | DESC
11 |
12 | s.homepage = 'https://github.com/wordpress-mobile/wpxmlrpc'
13 | s.license = { type: 'MIT', file: 'LICENSE.md' }
14 | s.author = { 'The WordPress Mobile Team' => 'mobile@wordpress.org' }
15 |
16 | s.ios.deployment_target = '11.0'
17 | s.osx.deployment_target = '10.13'
18 | s.swift_version = '5.0'
19 |
20 | s.source = { git: 'https://github.com/wordpress-mobile/wpxmlrpc.git', tag: s.version.to_s }
21 | s.source_files = 'WPXMLRPC'
22 | s.public_header_files = ['WPXMLRPC/WPXMLRPC.h', 'WPXMLRPC/WPXMLRPCEncoder.h', 'WPXMLRPC/WPXMLRPCDecoder.h']
23 | s.libraries = 'iconv'
24 |
25 | s.test_spec do |test|
26 | test.source_files = 'WPXMLRPCTest/Tests/*.{h,m}'
27 | test.resources = 'WPXMLRPCTest/Test Data/*'
28 | end
29 | end
30 |
--------------------------------------------------------------------------------