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