1 /**
2  * Authors: Szabo Bogdan <szabobogdan@yahoo.com>
3  * Date: 3 9, 2015
4  * License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
5  * Copyright: Public Domain
6  */
7 module vibedav.ifheader;
8 
9 import vibedav.parser;
10 
11 import std.stdio;
12 import std..string;
13 import core.vararg;
14 
15 struct IfCondition {
16 
17 	string condition;
18 	string etag;
19 	bool isNot;
20 
21 	this(string condition) {
22 		parse(condition);
23 	}
24 
25 	private void parse(string data) {
26 		void addConditionCallback(string value) {
27 			condition = value[1..$-1];
28 		}
29 
30 		void notConditionCallback(string value) {
31 			isNot = true;
32 		}
33 
34 		void etagConditionCallback(string value) {
35 			etag = value[2..$-2];
36 		}
37 
38 		Parser parser;
39 
40 		ParseInsideToken valueToken = new ParseInsideToken;
41 		valueToken.startChar = '<';
42 		valueToken.endChar = '>';
43 		valueToken.callback = &addConditionCallback;
44 
45 		ParseInsideToken etagToken = new ParseInsideToken;
46 		etagToken.startChar = '[';
47 		etagToken.endChar = ']';
48 		etagToken.callback = &etagConditionCallback;
49 
50 		ParseStaticToken notToken = new ParseStaticToken;
51 		notToken.value = "Not";
52 		notToken.callback = &notConditionCallback;
53 
54 		parser.tokens ~= valueToken;
55 		parser.tokens ~= notToken;
56 		parser.tokens ~= etagToken;
57 		parser.start(data);
58 	}
59 }
60 
61 struct IfHeader {
62 
63 	IfCondition[][string] list;
64 
65 	@property {
66 		bool isEmpty() {
67 			return list.keys.length == 0;
68 		}
69 	}
70 
71 	string getAttr(string href, ...) {
72 		string[] hrefList = [href];
73 
74 		//get the list of hrefs
75 		for (int i = 0; i < _arguments.length; i++) {
76 			if (_arguments[i] == typeid(string))
77 				hrefList ~= va_arg!(string)(_argptr);
78 			else
79 				throw new Exception("Invalid variadic type. string expected.");
80 		}
81 
82 		//find the first attribute
83 		foreach(attr; hrefList)
84 			if(attr in list && list[attr].length > 0)
85 				return list[attr][0].condition;
86 
87 		return "";
88 	}
89 
90 	bool has(string path, string condition) {
91 		if(path in list)
92 			foreach(IfCondition ifCondition; list[path]) {
93 				if(ifCondition.condition == condition && !ifCondition.isNot)
94 					return true;
95 			}
96 
97 		return false;
98 	}
99 
100 	bool hasNot(string path, string condition) {
101 		if(path in list)
102 			foreach(ifCondition; list[path])
103 				if(ifCondition.condition == condition && ifCondition.isNot)
104 					return true;
105 
106 		return false;
107 	}
108 
109 	bool[string] getLocks(string url) {
110 		bool[string] result;
111 
112 		if("" in list) {
113 			foreach(IfCondition ifCondition; list[""])
114 				if(!ifCondition.isNot)
115 					result[ifCondition.condition] = true;
116 		}
117 
118 		foreach(u, urlList; list) {
119 			if(url.indexOf(u) != -1)
120 				foreach(IfCondition ifCondition; list[u])
121 					if(!ifCondition.isNot)
122 						result[ifCondition.condition] = true;
123 		}
124 
125 		return result;
126 	}
127 
128 	static IfHeader parse(string data) {
129 		IfHeader ifHeader;
130 		string path = "";
131 
132 		void conditionTokenCallback(string data) {
133 			string condition = data[1..$-1];
134 
135 			if(path !in ifHeader.list)
136 				ifHeader.list[path] = [ IfCondition(condition) ];
137 			else
138 				ifHeader.list[path] ~= IfCondition(condition);
139 		}
140 
141 		void pathTokenCallback(string data) {
142 			path = data[1..$-1];
143 		}
144 
145 		Parser parser;
146 
147 		ParseInsideToken conditionToken = new ParseInsideToken;
148 		conditionToken.startChar = '(';
149 		conditionToken.endChar = ')';
150 		conditionToken.callback = &conditionTokenCallback;
151 
152 		ParseInsideToken pathToken = new ParseInsideToken;
153 		pathToken.startChar = '<';
154 		pathToken.endChar = '>';
155 		pathToken.callback = &pathTokenCallback;
156 
157 		parser.tokens ~= conditionToken;
158 		parser.tokens ~= pathToken;
159 
160 		if(data != "" && data != "()")
161 			parser.start(data);
162 
163 		return ifHeader;
164 	}
165 }
166 
167 @("No-Tag-List")
168 unittest {
169 	auto tag = IfHeader.parse("(<urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf>)");
170 
171 	assert(tag.list.keys.length == 1);
172 	assert(tag.has("", "urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf"));
173 }
174 
175 @("Tagged-List")
176 unittest {
177 	auto tag = IfHeader.parse("<http://example.com/locked/> (<urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf>) (<other>)");
178 
179 	assert(tag.list.keys.length == 1);
180 	assert(tag.has("http://example.com/locked/", "urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf"));
181 	assert(tag.has("http://example.com/locked/", "other"));
182 }
183 
184 @("Tagged-List")
185 unittest {
186 	auto tag = IfHeader.parse("<http://example.com/locked/> (<urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf>)");
187 
188 	assert(tag.list.keys.length == 1);
189 	assert(tag.has("http://example.com/locked/", "urn:uuid:150852e2-3847-42d5-8cbe-0f4f296f26cf"));
190 }
191 
192 @("multiple conditions")
193 unittest {
194 	auto tag = IfHeader.parse("(<urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2>) (Not <DAV:no-lock>)");
195 
196 	assert(tag.list.keys.length == 1);
197 	assert(tag.has("", "urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2"));
198 	assert(!tag.has("", "DAV:no-lock"));
199 	assert(tag.hasNot("", "DAV:no-lock"));
200 	assert(!tag.hasNot("", "urn:uuid:181d4fae-7d8c-11d0-a765-00a0c91e6bf2"));
201 }
202 
203 @("etag conditions")
204 unittest {
205 	auto tag = IfHeader.parse(`(<DAV:no-lock> ["C8E30A4F4684AB4A5053F6C1ACBA1023"])`);
206 
207 	assert(tag.list.keys.length == 1);
208 	assert(tag.has("", "DAV:no-lock"));
209 	assert(tag.list[""][0].etag == "C8E30A4F4684AB4A5053F6C1ACBA1023");
210 }