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