1 /*
2 * Copyright (c) 2020, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "lib/url/url.hpp"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "core/common/code_utils.hpp"
36
37 namespace ot {
38 namespace Url {
39
Init(char * aUrl)40 otError Url::Init(char *aUrl)
41 {
42 otError error = OT_ERROR_NONE;
43 char * url = aUrl;
44
45 mEnd = aUrl + strlen(aUrl);
46 mProtocol = aUrl;
47
48 url = strstr(aUrl, "://");
49 VerifyOrExit(url != nullptr, error = OT_ERROR_PARSE);
50 *url = '\0';
51 url += sizeof("://") - 1;
52 mPath = url;
53
54 url = strstr(url, "?");
55
56 if (url != nullptr)
57 {
58 mQuery = ++url;
59
60 for (char *cur = strtok(url, "&"); cur != nullptr; cur = strtok(nullptr, "&"))
61 {
62 cur[-1] = '\0';
63 }
64 }
65 else
66 {
67 mQuery = mEnd;
68 }
69
70 exit:
71 return error;
72 }
73
GetValue(const char * aName,const char * aLastValue) const74 const char *Url::GetValue(const char *aName, const char *aLastValue) const
75 {
76 const char * rval = nullptr;
77 const size_t len = strlen(aName);
78 const char * start;
79
80 if (aLastValue == nullptr)
81 {
82 start = mQuery;
83 }
84 else
85 {
86 VerifyOrExit(aLastValue > mQuery && aLastValue < mEnd);
87 start = aLastValue + strlen(aLastValue) + 1;
88 }
89
90 while (start < mEnd)
91 {
92 const char *last = nullptr;
93
94 if (!strncmp(aName, start, len))
95 {
96 if (start[len] == '=')
97 {
98 ExitNow(rval = &start[len + 1]);
99 }
100 else if (start[len] == '\0')
101 {
102 ExitNow(rval = &start[len]);
103 }
104 }
105 last = start;
106 start = last + strlen(last) + 1;
107 }
108
109 exit:
110 return rval;
111 }
112
113 } // namespace Url
114 } // namespace ot
115
116 #ifndef SELF_TEST
117 #define SELF_TEST 0
118 #endif
119
120 #if SELF_TEST
121 #include <assert.h>
122
123 #pragma GCC diagnostic push
124 #pragma GCC diagnostic ignored "-Warray-bounds"
125
TestSimple(void)126 void TestSimple(void)
127 {
128 char url[] = "spinel:///dev/ttyUSB0?baudrate=115200";
129 ot::Url::Url args;
130
131 assert(!args.Init(url));
132
133 assert(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
134 assert(!strcmp(args.GetValue("baudrate"), "115200"));
135 assert(args.GetValue("not-exists") == nullptr);
136 assert(args.GetValue("last-value-wrong-position", url) == nullptr);
137 assert(args.GetValue("last-value-before-url", url - 1) == nullptr);
138 assert(args.GetValue("last-value-after-url", url + sizeof(url)) == nullptr);
139
140 printf("PASS %s\r\n", __func__);
141 }
142
TestSimpleNoQueryString(void)143 void TestSimpleNoQueryString(void)
144 {
145 char url[] = "spinel:///dev/ttyUSB0";
146 ot::Url::Url args;
147
148 assert(!args.Init(url));
149 assert(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
150 assert(args.GetValue("last-value-wrong-position", url) == nullptr);
151 assert(args.GetValue("last-value-before-url", url - 1) == nullptr);
152 assert(args.GetValue("last-value-after-url", url + sizeof(url)) == nullptr);
153
154 printf("PASS %s\r\n", __func__);
155 }
156
TestEmptyValue(void)157 void TestEmptyValue(void)
158 {
159 char url[] = "spinel:///dev/ttyUSB0?rtscts&baudrate=115200&verbose&verbose&verbose";
160 ot::Url::Url args;
161 const char * arg = nullptr;
162
163 assert(!args.Init(url));
164 assert(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
165 assert((arg = args.GetValue("rtscts")) != nullptr);
166 assert(args.GetValue("rtscts", arg) == nullptr);
167 assert((arg = args.GetValue("verbose", arg)) != nullptr);
168 assert((arg = args.GetValue("verbose", arg)) != nullptr);
169 assert((arg = args.GetValue("verbose", arg)) != nullptr);
170 assert((arg = args.GetValue("verbose", arg)) == nullptr);
171
172 printf("PASS %s\r\n", __func__);
173 }
174
TestMultipleProtocols(void)175 void TestMultipleProtocols(void)
176 {
177 char url[] = "spinel+spi:///dev/ttyUSB0?baudrate=115200";
178 ot::Url::Url args;
179
180 assert(!args.Init(url));
181 assert(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
182 assert(!strcmp(args.GetValue("baudrate"), "115200"));
183
184 printf("PASS %s\r\n", __func__);
185 }
186
TestMultipleProtocolsAndDuplicateParameters(void)187 void TestMultipleProtocolsAndDuplicateParameters(void)
188 {
189 char url[] = "spinel+exec:///path/to/ot-rcp?arg=1&arg=arg2&arg=3";
190 ot::Url::Url args;
191 const char * arg = nullptr;
192
193 assert(!args.Init(url));
194 assert(!strcmp(args.GetPath(), "/path/to/ot-rcp"));
195
196 arg = args.GetValue("arg");
197 assert(!strcmp(arg, "1"));
198
199 arg = args.GetValue("arg", arg);
200 assert(!strcmp(arg, "arg2"));
201
202 arg = args.GetValue("arg", arg);
203 assert(!strcmp(arg, "3"));
204
205 assert(args.GetValue("arg", url) == nullptr);
206 assert(args.GetValue("arg", url - 1) == nullptr);
207 assert(args.GetValue("arg", url + sizeof(url)) == nullptr);
208
209 printf("PASS %s\r\n", __func__);
210 }
211
212 #pragma GCC diagnostic pop
213
main(void)214 int main(void)
215 {
216 TestSimple();
217 TestSimpleNoQueryString();
218 TestEmptyValue();
219 TestMultipleProtocols();
220 TestMultipleProtocolsAndDuplicateParameters();
221
222 return 0;
223 }
224
225 #endif // SELF_TEST
226