Imported Upstream version 15.0.0
[platform/upstream/libzypp.git] / zypp / url / UrlUtils.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /**
10  * \file zypp/url/UrlUtils.cc
11  */
12 #include "zypp/base/Gettext.h"
13 #include "zypp/base/String.h"
14 #include "zypp/url/UrlUtils.h"
15
16 #include <stdlib.h>   // strtol
17 #include <cctype>     // isxdigit
18 #include <stdexcept>
19
20
21 //////////////////////////////////////////////////////////////////////
22 namespace zypp
23 { ////////////////////////////////////////////////////////////////////
24
25   ////////////////////////////////////////////////////////////////////
26   namespace url
27   { //////////////////////////////////////////////////////////////////
28
29
30     // ---------------------------------------------------------------
31     std::string
32     encode(const std::string &str, const std::string &safe,
33                                    EEncoding         eflag)
34     {
35       std::string skip("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
36                        "abcdefghijklmnopqrstuvwxyz"
37                        "0123456789.~_-");
38       std::string more(":/?#[]@!$&'()*+,;=");
39       size_t      beg, pos, len;
40       std::string out;
41
42       for(size_t i=0; i<safe.size(); i++)
43       {
44         if( more.find(safe.at(i)) != std::string::npos)
45           skip.append(1, safe.at(i));
46       }
47
48       len = str.length();
49       beg = 0;
50       while( beg < len)
51       {
52         pos = str.find_first_not_of(skip, beg);
53         if(pos != std::string::npos)
54         {
55           if( pos > beg)
56           {
57             out.append(str, beg, pos - beg);
58           }
59
60           if( eflag == E_ENCODED &&
61               pos + 2 < len      &&
62               str.at(pos) == '%' &&
63               std::isxdigit(str.at(pos + 1)) &&
64               std::isxdigit(str.at(pos + 2)))
65           {
66             out.append(str, pos, 3);
67             beg = pos + 3;
68           }
69           else
70           {
71             out.append( encode_octet( str.at(pos)));
72             beg = pos + 1;
73           }
74         }
75         else
76         {
77           out.append(str, beg, len - beg);
78           beg = len;
79         }
80       }
81       return out;
82     }
83
84
85     // ---------------------------------------------------------------
86     std::string
87     decode(const std::string &str, bool allowNUL)
88     {
89       size_t      pos, end, len;
90       std::string out(str);
91
92       len = out.length();
93       pos = end = 0;
94       while(pos < len)
95       {
96         out[end] = out[pos];
97         if( pos + 2 < len && out.at(pos) == '%')
98         {
99           int c = decode_octet(out.c_str() + pos + 1);
100           switch(c)
101           {
102             case -1:
103               // not a hex noted octet...
104             break;
105
106             case 0:
107               // is a %00 octet allowed ?
108               if( !allowNUL)
109               {
110                 ZYPP_THROW(UrlDecodingException(
111                   _("Encoded string contains a NUL byte")
112                 ));
113               }
114             default:
115               // other octets are fine...
116               out[end] = c;
117               pos += 2;
118             break;
119           }
120         }
121         pos++;
122         end++;
123       }
124       if( end < pos)
125         out.erase(end);
126       return out;
127     }
128
129
130     // ---------------------------------------------------------------
131     std::string
132     encode_octet(const unsigned char c)
133     {
134       static const unsigned char tab[] = "0123456789ABCDEF";
135       unsigned char      out[4];
136
137       out[0] = '%';
138       out[1] = tab[0x0f & (c >> 4)];
139       out[2] = tab[0x0f & c];
140       out[3] = '\0';
141
142       //snprintf(out, sizeof(out), "%%%02X", c);
143       return std::string((char *)out);
144     }
145
146
147     // ---------------------------------------------------------------
148     int
149     decode_octet(const char *hex)
150     {
151       if(hex && std::isxdigit(hex[0]) && std::isxdigit(hex[1]))
152       {
153         char x[3] = { hex[0], hex[1], '\0'};
154         return 0xff & ::strtol(x, NULL, 16);
155       }
156       else
157       {
158         return -1;
159       }
160     }
161
162
163     // ---------------------------------------------------------------
164     void
165     split(ParamVec          &pvec,
166           const std::string &pstr,
167                 const std::string &psep)
168     {
169       size_t beg, pos, len;
170       if( psep.empty())
171       {
172         ZYPP_THROW(UrlNotSupportedException(
173           _("Invalid parameter array split separator character")
174         ));
175       }
176
177       len = pstr.length();
178       beg = 0;
179
180       while( beg < len)
181       {
182         pos = pstr.find(psep, beg);
183         if(pos != std::string::npos)
184         {
185           pvec.push_back( pstr.substr(beg, pos - beg));
186           beg = pos + 1;
187         }
188         else
189         {
190           pvec.push_back( pstr.substr(beg, len - beg));
191           beg = len;
192         }
193       }
194     }
195
196
197     // ---------------------------------------------------------------
198     void
199     split(ParamMap          &pmap,
200           const std::string &str,
201           const std::string &psep,
202           const std::string &vsep,
203           EEncoding         eflag)
204     {
205       ParamVec                 pvec;
206       ParamVec::const_iterator pitr;
207       std::string              k, v;
208       size_t                   pos;
209
210       if( psep.empty() || vsep.empty())
211       {
212         ZYPP_THROW(UrlNotSupportedException(
213           _("Invalid parameter map split separator character")
214         ));
215       }
216
217       split(pvec, str, psep);
218
219       for( pitr = pvec.begin(); pitr != pvec.end(); ++pitr)
220       {
221         pos = pitr->find(vsep);
222         if(pos != std::string::npos)
223         {
224           if( eflag == E_DECODED)
225           {
226             k = url::decode(pitr->substr(0, pos));
227             v = url::decode(pitr->substr(pos + 1));
228             pmap[ k ] = v;
229           }
230           else
231           {
232             k = pitr->substr(0, pos);
233             v = pitr->substr(pos + 1);
234             pmap[ k ] = v;
235           }
236         }
237         else
238         {
239           if( eflag == E_DECODED)
240           {
241             pmap[ url::decode(*pitr) ] = "";
242           }
243           else
244           {
245             pmap[ *pitr ] = "";
246           }
247         }
248       }
249     }
250
251
252     // ---------------------------------------------------------------
253     std::string
254     join(const ParamVec    &pvec,
255          const std::string &psep)
256     {
257       std::string    str;
258       ParamVec::const_iterator i( pvec.begin());
259
260       if( i != pvec.end())
261       {
262         str = *i;
263         while( ++i != pvec.end())
264         {
265           str += psep + *i;
266         }
267       }
268
269       return str;
270     }
271
272
273     // ---------------------------------------------------------------
274     std::string
275     join(const ParamMap    &pmap,
276          const std::string &psep,
277          const std::string &vsep,
278          const std::string &safe)
279     {
280       if( psep.empty() || vsep.empty())
281       {
282         ZYPP_THROW(UrlNotSupportedException(
283           _("Invalid parameter array join separator character")
284         ));
285       }
286
287       std::string join_safe;
288       for(std::string::size_type i=0; i<safe.size(); i++)
289       {
290         if( psep.find(safe[i]) == std::string::npos &&
291             vsep.find(safe[i]) == std::string::npos)
292         {
293           join_safe.append(1, safe[i]);
294         }
295       }
296       std::string              str;
297       ParamMap::const_iterator i( pmap.begin());
298
299       if( i != pmap.end())
300       {
301         str = encode(i->first, join_safe);
302         if( !i->second.empty())
303           str += vsep + encode(i->second, join_safe);
304
305         while( ++i != pmap.end())
306         {
307           str += psep + encode(i->first, join_safe);
308           if( !i->second.empty())
309             str +=  vsep + encode(i->second, join_safe);
310         }
311       }
312
313       return str;
314     }
315
316
317     //////////////////////////////////////////////////////////////////
318   } // namespace url
319   ////////////////////////////////////////////////////////////////////
320
321   ////////////////////////////////////////////////////////////////////
322 } // namespace zypp
323 //////////////////////////////////////////////////////////////////////
324 /*
325 ** vim: set ts=2 sts=2 sw=2 ai et:
326 */