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