1 /* Copyright (C) 2000-2015 Lavtech.com corp. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18 #include "udm_config.h"
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28
29 #include <fcntl.h>
30
31 #ifdef WIN32
32 #include <io.h>
33 #endif
34
35
36 #include "udm_common.h"
37 #include "udm_proto.h"
38 #include "udm_utils.h"
39 #include "udm_vars.h"
40 #include "udm_log.h"
41 #include "udm_http.h"
42 #include "udm_hash.h"
43
44 #ifdef NAME_MAX
45 #define MIRROR_NAME_MAX NAME_MAX
46 #else
47 #define MIRROR_NAME_MAX 255
48 #endif
49
50 typedef struct
51 {
52 const char *data;
53 const char *hdrs;
54 size_t name_max;
55 int period;
56 } UDM_MIRROR_PARAM;
57
58
59 static void
UdmMirrorParamInit(UDM_MIRROR_PARAM * M,UDM_AGENT * A,UDM_DOCUMENT * D)60 UdmMirrorParamInit(UDM_MIRROR_PARAM *M, UDM_AGENT *A, UDM_DOCUMENT *D)
61 {
62 M->name_max= (int) UdmVarListFindInt(&A->Conf->Vars, "MirrorNameMax", MIRROR_NAME_MAX);
63 M->period= UdmVarListFindInt(&D->Sections, "MirrorPeriod", -1);
64 M->data= UdmVarListFindStr(&D->Sections,"MirrorRoot", NULL);
65 M->hdrs= UdmVarListFindStr(&D->Sections,"MirrorHeadersRoot", NULL);
66 }
67
68
69 static size_t
udm_mirror_filename(char * str,size_t size,size_t name_max,const char * prefix,const UDM_URL * url,const char * suffix1,const char * suffix2)70 udm_mirror_filename(char *str, size_t size, size_t name_max,
71 const char *prefix, const UDM_URL *url,
72 const char *suffix1, const char *suffix2)
73 {
74 size_t len= udm_snprintf(str, size, "%s"UDMSLASHSTR"%s"UDMSLASHSTR"%s%s%s%s",
75 prefix,
76 UDM_NULL2EMPTY(url->schema),
77 UDM_NULL2EMPTY(url->hostname),
78 UDM_NULL2EMPTY(url->path), suffix1, suffix2);
79 if (name_max < len)
80 {
81 udmhash64_t hash= UdmHash64(suffix1, strlen(suffix1));
82 unsigned int hi= hash >> 32;
83 unsigned int lo= hash & 0xFFFFFFFFULL;
84 len= udm_snprintf(str, size, "%s"UDMSLASHSTR"%s"UDMSLASHSTR"%s%s%08X%08X%s",
85 prefix,
86 UDM_NULL2EMPTY(url->schema),
87 UDM_NULL2EMPTY(url->hostname),
88 UDM_NULL2EMPTY(url->path),
89 hi, lo, suffix2);
90 }
91 return len;
92 }
93
94
95 static size_t
udm_mirror_dirname(char * str,size_t size,const char * prefix,const UDM_URL * url)96 udm_mirror_dirname(char *str, size_t size,
97 const char *prefix, const UDM_URL *url)
98 {
99 return udm_snprintf(str, size,"%s"UDMSLASHSTR"%s"UDMSLASHSTR"%s%s",
100 prefix,
101 UDM_NULL2EMPTY(url->schema),
102 UDM_NULL2EMPTY(url->hostname),
103 UDM_NULL2EMPTY(url->path));
104 }
105
106
107 static size_t
udm_mirror_urlpart_length(const UDM_URL * url)108 udm_mirror_urlpart_length(const UDM_URL *url)
109 {
110 return strlen(UDM_NULL2EMPTY(url->schema)) +
111 strlen(UDM_NULL2EMPTY(url->hostname)) +
112 strlen(UDM_NULL2EMPTY(url->path));
113 }
114
115
116 UDM_API(udm_rc_t)
UdmMirrorGET(UDM_AGENT * Indexer,UDM_DOCUMENT * Doc,UDM_URL * url)117 UdmMirrorGET(UDM_AGENT *Indexer, UDM_DOCUMENT *Doc, UDM_URL *url)
118 {
119 udm_rc_t rc= UDM_OK;
120 int fbody, fheader, have_headers= 0;
121 char *str, *estr;
122 struct stat sb;
123 time_t nowtime= time(NULL);
124 size_t str_len, estr_len;
125 UDM_MIRROR_PARAM Mirror;
126
127 UdmMirrorParamInit(&Mirror, Indexer, Doc);
128
129 if (Mirror.period <= 0)
130 return UDM_MIRROR_NOT_FOUND;
131
132 /* MirrorRoot is not specified, nothing to do */
133 if (!Mirror.data)
134 {
135 UdmLog(Indexer, UDM_LOG_ERROR, "MirrorGet: MirrorRoot is not set");
136 return UDM_MIRROR_NOT_FOUND;
137 }
138
139 str_len= 128 + strlen(Mirror.data) +
140 ((Mirror.hdrs) ? strlen(Mirror.hdrs) : 0) +
141 udm_mirror_urlpart_length(url) +
142 (estr_len= (url->filename && url->filename[0]) ?
143 3 * strlen(url->filename) + 1 : 16);
144
145 if ((str= (char*) UdmMalloc(str_len)) == NULL)
146 return UDM_MIRROR_NOT_FOUND;
147
148 if ((estr= (char*) UdmMalloc(estr_len)) == NULL)
149 {
150 UdmFree(str);
151 return UDM_MIRROR_NOT_FOUND;
152 }
153
154
155 UdmURLEncode(estr, str, udm_snprintf(str, str_len, "%s",
156 url->filename && strlen(url->filename) ?
157 url->filename : "index.html"));
158
159 udm_mirror_filename(str, str_len, Mirror.name_max, Mirror.data, url, estr, ".body");
160 if ((fbody= open(str, O_RDONLY|UDM_BINARY)) == -1)
161 {
162 UdmLog(Indexer, UDM_LOG_EXTRA, "Mirror file %s not found", str);
163 rc= UDM_MIRROR_NOT_FOUND;
164 goto end;
165 }
166
167 /* Check on file mtime > days ? return */
168 if (fstat(fbody, &sb))
169 {
170 rc= UDM_MIRROR_NOT_FOUND;
171 goto end;
172 }
173
174 if (nowtime > sb.st_mtime + Mirror.period)
175 {
176 close(fbody);
177 UdmLog(Indexer, UDM_LOG_EXTRA, "%s is older then %d secs", str, Mirror.period);
178 rc= UDM_MIRROR_EXPIRED;
179 goto end;
180 }
181
182 if (Mirror.hdrs)
183 {
184 udm_mirror_filename(str, str_len, Mirror.name_max, Mirror.hdrs, url, estr, ".header");
185 if ((fheader= open(str, O_RDONLY|UDM_BINARY)) >= 0)
186 {
187 UdmHTTPBufReset(&Doc->Buf);
188 UdmHTTPBufAppendFromFile(&Doc->Buf, fheader);
189 close(fheader);
190 have_headers= 1;
191 }
192 }
193
194 if (!have_headers)
195 {
196 /* header file not found, get body as file method */
197 UdmHTTPBufReset(&Doc->Buf);
198 UdmHTTPBufAppend(&Doc->Buf, UDM_CSTR_WITH_LEN("HTTP/1.0 200 OK\r\n\r\n"));
199 }
200
201 rc= UdmHTTPBufAppendFromFile(&Doc->Buf, fbody);
202 close(fbody);
203
204 end:
205 UdmFree(estr);
206 UdmFree(str);
207 return rc;
208 }
209
210
211 static udm_rc_t
UdmMirrorWriteToFile(UDM_AGENT * Indexer,char * str,size_t str_len,size_t name_max,const char * prefix,const UDM_URL * url,const char * suffix1,const char * suffix2,const char * src,size_t srclen)212 UdmMirrorWriteToFile(UDM_AGENT *Indexer,
213 char *str, size_t str_len, size_t name_max,
214 const char *prefix,
215 const UDM_URL *url,
216 const char *suffix1,
217 const char *suffix2,
218 const char *src, size_t srclen)
219 {
220 int fd;
221 size_t dirlen; /* Can't assign here */
222 udm_mirror_dirname(str, str_len, prefix, url);
223 if (UdmBuild(str, 0755) != 0) /* TODO: fix UdmBuild() not to change "str" */
224 {
225 UdmLog(Indexer, UDM_LOG_ERROR, "Can't create dir %s", str);
226 return UDM_MIRROR_CANT_BUILD;
227 }
228
229 dirlen= strlen(str); /* Note, UdmBuild() removes trailing slash from "str" */
230 UDM_ASSERT(dirlen < str_len);
231 if (name_max < (dirlen + udm_snprintf(str + dirlen, str_len - dirlen,
232 "%s%s%s", UDMSLASHSTR,
233 suffix1, suffix2)))
234 udm_mirror_filename(str, str_len, name_max, prefix, url, suffix1, suffix2);
235
236 if ((fd= open(str, O_CREAT|O_WRONLY|UDM_BINARY,UDM_IWRITE)) == -1)
237 {
238 UdmLog(Indexer, UDM_LOG_EXTRA, "Can't open mirror file %s", str);
239 return UDM_MIRROR_CANT_OPEN;
240 }
241 write(fd, src, srclen);
242 close(fd);
243 return UDM_OK;
244 }
245
246
247 UDM_API(udm_rc_t)
UdmMirrorPUT(UDM_AGENT * Indexer,UDM_DOCUMENT * Doc,UDM_URL * url)248 UdmMirrorPUT(UDM_AGENT *Indexer, UDM_DOCUMENT *Doc, UDM_URL *url)
249 {
250 char *str, *estr;
251 size_t str_len, estr_len;
252 size_t content_offset;
253 udm_rc_t rc= UDM_OK;
254 UDM_MIRROR_PARAM Mirror;
255
256 UdmMirrorParamInit(&Mirror, Indexer, Doc);
257
258 if (!Mirror.data)
259 {
260 UdmLog(Indexer, UDM_LOG_ERROR, "MirrorPUT: MirrorRoot is not set");
261 return UDM_ERROR;
262 }
263
264 /* Find HTTP header end */
265 if (!(content_offset= UdmHTTPBufFindContent(&Doc->Buf)))
266 return UDM_ERROR;
267
268 str_len= 128 + strlen(Mirror.data) +
269 (Mirror.hdrs ? strlen(Mirror.hdrs) : 0) +
270 udm_mirror_urlpart_length(url) +
271 (estr_len= (url->filename && url->filename[0]) ?
272 3 * strlen(url->filename) + 1 : 16);
273
274 if ((str= (char*)UdmMalloc(str_len)) == NULL)
275 return UDM_MIRROR_CANT_BUILD;
276
277 if ((estr= (char*) UdmMalloc(estr_len)) == NULL)
278 {
279 UdmFree(str);
280 return UDM_MIRROR_CANT_BUILD;
281 }
282
283
284 UdmURLEncode(estr, str, udm_snprintf(str, str_len, "%s",
285 (url->filename && strlen(url->filename)) ?
286 url->filename : "index.html"));
287
288 /* Put Content if MirrorRoot is specified */
289 if (Mirror.data)
290 {
291 UDM_CONST_STR content;
292 if (UdmHTTPBufContentToConstStr(&Doc->Buf, &content))
293 {
294 rc= UDM_ERROR;
295 goto end;
296 }
297 if (UDM_OK != (rc= UdmMirrorWriteToFile(Indexer, str, str_len,
298 Mirror.name_max, Mirror.data,
299 url, estr, ".body",
300 content.str, content.length)))
301 goto end;
302 }
303
304 /* Put Headers if MirrorHeadersRoot is specified */
305 if (Mirror.hdrs)
306 {
307 const char *src= UdmHTTPBufPtr(&Doc->Buf);
308 size_t srclen= UdmHTTPBufHeaderSize(&Doc->Buf);
309 if (UDM_OK != (rc= UdmMirrorWriteToFile(Indexer, str, str_len,
310 Mirror.name_max, Mirror.hdrs,
311 url, estr, ".header",
312 src, srclen)))
313 goto end;
314 }
315
316 end:
317 UdmFree(estr);
318 UdmFree(str);
319 return rc;
320 }
321