1 /*
2  *  IceWM - URL decoder
3  *  Copyright (C) 2001 The Authors of IceWM
4  *
5  *  Release under terms of the GNU Library General Public License
6  *
7  *  2001/02/25: Mathias Hasselmann <mathias.hasselmann@gmx.net>
8  *  - initial version
9  */
10 
11 #include "config.h"
12 #include "yurl.h"
13 #include "base.h"
14 #include "intl.h"
15 #include "binascii.h"
16 #include "ypointer.h"
17 #include <sys/types.h>
18 #include <regex.h>
19 
20 class Matches {
21 private:
22     mstring const str;
23     int const size;
24     asmart<regmatch_t> const match;
25 public:
Matches(mstring s,int n)26     Matches(mstring s, int n) : str(s), size(n), match(new regmatch_t[n]) {}
num() const27     int num() const { return size; }
beg(int i) const28     int beg(int i) const { return match[i].rm_so; }
end(int i) const29     int end(int i) const { return match[i].rm_eo; }
len(int i) const30     int len(int i) const { return end(i) - beg(i); }
has(int i) const31     bool has(int i) const { return 0 <= beg(i) && beg(i) <= end(i); }
get(int i) const32     mstring get(int i) const { return str.substring(beg(i), len(i)); }
operator [](int i) const33     mstring operator[](int i) const { return has(i) ? get(i) : null; }
ptr() const34     regmatch_t* ptr() const { return match; }
35 };
36 
37 class Pattern {
38 private:
39     regex_t pat;
40     int comp;
41     int exec;
42 public:
Pattern(const char * re)43     Pattern(const char* re) :
44         comp(regcomp(&pat, re, REG_EXTENDED | REG_NEWLINE)),
45         exec(0)
46     {
47         if (comp) {
48             char err[99] = "";
49             regerror(comp, &pat, err, sizeof err);
50             warn("regcomp failed for %s: %s\n", re, err);
51         }
52     }
~Pattern()53     ~Pattern() {
54         if (0 == comp)
55             regfree(&pat);
56     }
match(const char * str,const Matches & m)57     bool match(const char* str, const Matches& m) {
58         if (0 == comp)
59             exec = regexec(&pat, str, m.num(), m.ptr(), 0);
60         return *this;
61     }
operator bool() const62     operator bool() const { return !(comp | exec); }
63 };
64 
65 /*******************************************************************************
66  * An URL decoder
67  ******************************************************************************/
68 
YURL()69 YURL::YURL() {
70 }
71 
YURL(mstring url)72 YURL::YURL(mstring url) {
73     *this = url;
74 }
75 
operator =(mstring url)76 void YURL::operator=(mstring url) {
77     scheme = null;
78     user = null;
79     pass = null;
80     host = null;
81     port = null;
82     path = null;
83 
84     // parse scheme://[user[:password]@]server[:port][/path]
85 
86     enum {
87         Path = 1, File = 2, Scheme = 3, User = 5, Pass = 7,
88         Host = 8, Port = 10, Inbox = 11, Count = 12,
89     };
90     const char re[] =
91         // 0:
92         "^"
93         // 1: path
94         "(/.*)"
95         "|"
96         // 2: file
97         "file://(.*)"
98         "|"
99         // 3: scheme
100         "([a-z][a-z0-9]+)"
101         "://"
102         // 4:
103         "("
104         // 5: user
105         "([^:/@]+)"
106         // 6:
107         "(:"
108         // 7: pass
109         "([^:/@]*)"
110         ")?"
111         "@)?"
112         // 8: host
113         "([^:@/]+)"
114         // 9:
115         "(:"
116         // 10: port
117         "([0-9]+|[a-z][a-z0-9]+)"
118         ")?"
119         // 11: inbox
120         "(/.*)?"
121         "$";
122 
123     mstring str(url);
124     Matches mat(str, Count);
125     Pattern rex(re);
126 
127     if (rex.match(str, mat)) {
128         if (mat.has(Path)) {
129             path = mat[Path];
130             scheme = "file";
131         }
132         else if (mat.has(File)) {
133             path = unescape(mat[File]);
134             scheme = "file";
135         }
136         else if (mat.has(Scheme)) {
137             scheme = mat[Scheme];
138             user = unescape(mat[User]);
139             pass = unescape(mat[Pass]);
140             host = unescape(mat[Host]);
141             port = mat[Port];
142             path = unescape(mat[Inbox]);
143         }
144     } else {
145         warn(_("Failed to parse URL \"%s\"."), str.c_str());
146     }
147 }
148 
unescape(mstring str)149 mstring YURL::unescape(mstring str) {
150     if (0 <= str.indexOf('%')) {
151         csmart nstr(new char[str.length()]);
152         if (nstr == nullptr)
153             return null;
154         char *d = nstr;
155 
156         for (unsigned i = 0; i < str.length(); i++) {
157             int c = str.charAt(i);
158 
159             if (c == '%') {
160                 if (i + 3 > str.length()) {
161                     warn(_("Incomplete hex escape in URL at position %d."),
162                             int(i + str.offset()));
163                     return null;
164                 }
165                 int a = BinAscii::unhex(str.charAt(i + 1));
166                 int b = BinAscii::unhex(str.charAt(i + 2));
167                 if (a == -1 || b == -1) {
168                     warn(_("Invalid hex escape in URL at position %d."),
169                             int(i + str.offset()));
170                     return null;
171                 }
172                 i += 2;
173                 c = (char)((a << 4) + b);
174             }
175             *d++ = (char)c;
176         }
177         str = mstring(nstr, d - nstr);
178     }
179     return str;
180 }
181 
182 // vim: set sw=4 ts=4 et:
183