1 /*
2 * Copyright (C) 1998,1999 Ross Combs (rocombs@cs.nmsu.edu)
3 * Copyright (C) 1999 Rob Crittenden (rcrit@greyoak.com)
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 #include "common/setup_before.h"
20 #include <stdio.h>
21 #ifdef HAVE_STDDEF_H
22 # include <stddef.h>
23 #else
24 # ifndef NULL
25 # define NULL ((void *)0)
26 # endif
27 #endif
28 #ifdef STDC_HEADERS
29 # include <stdlib.h>
30 #else
31 # ifdef HAVE_MALLOC_H
32 # include <malloc.h>
33 # endif
34 #endif
35 #ifdef HAVE_STRING_H
36 # include <string.h>
37 #else
38 # ifdef HAVE_STRINGS_H
39 # include <strings.h>
40 # endif
41 #endif
42 #include "compat/strchr.h"
43 #include <errno.h>
44 #include "compat/strerror.h"
45 #ifdef HAVE_SYS_TYPES_H
46 # include <sys/types.h>
47 #endif
48 #ifdef HAVE_SYS_STAT_H
49 # include <sys/stat.h>
50 #endif
51 #include "common/bn_type.h"
52 #include "common/queue.h"
53 #include "connection.h"
54 #include "common/packet.h"
55 #include "common/file_protocol.h"
56 #include "common/eventlog.h"
57 #include "prefs.h"
58 #include "common/bnettime.h"
59 #include "common/util.h"
60 #include "common/xalloc.h"
61 #include "file.h"
62 #include "common/setup_after.h"
63
64
65 static char const * file_get_info(char const * rawname, unsigned int * len, bn_long * modtime);
66
file_find_default(const char * rawname)67 static char * file_find_default(const char *rawname)
68 {
69 /* Add new default files here */
70 const char * defaultfiles[]={"termsofservice-",".txt",
71 "newaccount-",".txt",
72 "chathelp-war3-",".txt",
73 "matchmaking-war3-",".dat",
74 "tos_",".txt",
75 "tos-unicode_", ".txt",
76 NULL,NULL};
77 const char ** pattern, **extension;
78 char *filename = NULL;
79
80 for (pattern = defaultfiles, extension = defaultfiles + 1; *pattern; pattern+=2, extension+=2)
81 if (!strncmp(rawname, *pattern,strlen(*pattern))) { /* Check if there is a default file available for this kind of file */
82 filename = (char*)xmalloc(strlen(prefs_get_filedir()) + 1 + strlen(*pattern) + 7 + strlen(*extension) + 1);
83
84 strcpy(filename, prefs_get_filedir());
85 strcat(filename, "/");
86 strcat(filename, *pattern);
87 strcat(filename, "default");
88 strcat(filename, *extension);
89
90 break;
91 }
92
93 return filename;
94 }
95
file_get_info(char const * rawname,unsigned int * len,bn_long * modtime)96 static char const * file_get_info(char const * rawname, unsigned int * len, bn_long * modtime)
97 {
98 char *filename;
99 struct stat sfile;
100 t_bnettime bt;
101
102 if (!rawname) {
103 eventlog(eventlog_level_error,__FUNCTION__,"got NULL rawname");
104 return NULL;
105 }
106
107 if (!len) {
108 eventlog(eventlog_level_error,__FUNCTION__,"got NULL len");
109 return NULL;
110 }
111
112 if (!modtime) {
113 eventlog(eventlog_level_error,__FUNCTION__,"got NULL modtime");
114 return NULL;
115 }
116
117 if (strchr(rawname,'/') || strchr(rawname,'\\')) {
118 eventlog(eventlog_level_warn,__FUNCTION__,"got rawname containing '/' or '\\' \"%s\"",rawname);
119 return NULL;
120 }
121
122 filename = buildpath(prefs_get_filedir(), rawname);
123
124 if (stat(filename,&sfile)<0) { /* if it doesn't exist, try to replace with default file */
125 xfree((void*)filename);
126 filename = file_find_default(rawname);
127 if (!filename) return NULL; /* no default version */
128
129 if (stat(filename,&sfile)<0) { /* try again */
130 /* FIXME: check for lower-case version of filename */
131 xfree(filename);
132 return NULL;
133 }
134 }
135
136 *len = (unsigned int)sfile.st_size;
137 bt = time_to_bnettime(sfile.st_mtime,0);
138 bnettime_to_bn_long(bt,modtime);
139
140 return filename;
141 }
142
143
file_to_mod_time(char const * rawname,bn_long * modtime)144 extern int file_to_mod_time(char const * rawname, bn_long * modtime)
145 {
146 char const * filename;
147 unsigned int len;
148
149 if (!rawname)
150 {
151 eventlog(eventlog_level_error,__FUNCTION__,"got NULL rawname");
152 return -1;
153 }
154 if (!modtime)
155 {
156 eventlog(eventlog_level_error,__FUNCTION__,"got NULL modtime");
157 return -1;
158 }
159
160 if (!(filename = file_get_info(rawname, &len, modtime)))
161 return -1;
162
163 xfree((void *)filename); /* avoid warning */
164
165 return 0;
166 }
167
168
169 /* Send a file. If the file doesn't exist we still need to respond
170 * to the file request. This will set filelen to 0 and send the server
171 * reply message and the client will be happy and not hang.
172 */
file_send(t_connection * c,char const * rawname,unsigned int adid,unsigned int etag,unsigned int startoffset,int need_header)173 extern int file_send(t_connection * c, char const * rawname, unsigned int adid, unsigned int etag, unsigned int startoffset, int need_header)
174 {
175 char const * filename;
176 t_packet * rpacket;
177 FILE * fp;
178 unsigned int filelen;
179 int nbytes;
180
181 if (!c)
182 {
183 eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection");
184 return -1;
185 }
186 if (!rawname)
187 {
188 eventlog(eventlog_level_error,__FUNCTION__,"got NULL rawname");
189 return -1;
190 }
191
192 if (!(rpacket = packet_create(packet_class_file)))
193 {
194 eventlog(eventlog_level_error,__FUNCTION__,"could not create file packet");
195 return -1;
196 }
197 packet_set_size(rpacket,sizeof(t_server_file_reply));
198 packet_set_type(rpacket,SERVER_FILE_REPLY);
199
200 if ((filename = file_get_info(rawname,&filelen,&rpacket->u.server_file_reply.timestamp)))
201 {
202 if (!(fp = fopen(filename,"rb")))
203 {
204 /* FIXME: check for lower-case version of filename */
205 eventlog(eventlog_level_error,__FUNCTION__,"stat() succeeded yet could not open file \"%s\" for reading (fclose: %s)",filename,pstrerror(errno));
206 filelen = 0;
207 }
208 xfree((void *)filename); /* avoid warning */
209 }
210 else
211 {
212 fp = NULL;
213 filelen = 0;
214 bn_long_set_a_b(&rpacket->u.server_file_reply.timestamp,0,0);
215 }
216
217 if (fp)
218 {
219 if (startoffset<filelen) {
220 fseek(fp,startoffset,SEEK_SET);
221 } else {
222 eventlog(eventlog_level_warn,__FUNCTION__,"[%d] startoffset is beyond end of file (%u>%u)",conn_get_socket(c),startoffset,filelen);
223 /* Keep the real filesize. Battle.net does it the same way ... */
224 fclose(fp);
225 fp = NULL;
226 }
227 }
228
229 if (need_header)
230 {
231 /* send the header from the server with the rawname and length. */
232 bn_int_set(&rpacket->u.server_file_reply.filelen,filelen);
233 bn_int_set(&rpacket->u.server_file_reply.adid,adid);
234 bn_int_set(&rpacket->u.server_file_reply.extensiontag,etag);
235 /* rpacket->u.server_file_reply.timestamp is set above */
236 packet_append_string(rpacket,rawname);
237 conn_push_outqueue(c,rpacket);
238 }
239 packet_del_ref(rpacket);
240
241 /* Now send the data. Since it may be longer than a packet; we use
242 * the raw packet class.
243 */
244 if (!fp)
245 {
246 eventlog(eventlog_level_warn,__FUNCTION__,"[%d] sending no data for file \"%s\"",conn_get_socket(c),rawname);
247 return -1;
248 }
249
250 eventlog(eventlog_level_info,__FUNCTION__,"[%d] sending file \"%s\" of length %d",conn_get_socket(c),rawname,filelen);
251 for (;;)
252 {
253 if (!(rpacket = packet_create(packet_class_raw)))
254 {
255 eventlog(eventlog_level_error,__FUNCTION__,"could not create raw packet");
256 if (fclose(fp)<0)
257 eventlog(eventlog_level_error,__FUNCTION__,"could not close file \"%s\" after reading (fclose: %s)",rawname,pstrerror(errno));
258 return -1;
259 }
260 if ((nbytes = fread(packet_get_raw_data_build(rpacket,0),1,MAX_PACKET_SIZE,fp))<(int)MAX_PACKET_SIZE)
261 {
262 if (nbytes>0) /* send out last portion */
263 {
264 packet_set_size(rpacket,nbytes);
265 conn_push_outqueue(c,rpacket);
266 }
267 packet_del_ref(rpacket);
268 if (ferror(fp))
269 eventlog(eventlog_level_error,__FUNCTION__,"read failed before EOF on file \"%s\" (fread: %s)",rawname,pstrerror(errno));
270 break;
271 }
272 packet_set_size(rpacket,nbytes);
273 conn_push_outqueue(c,rpacket);
274 packet_del_ref(rpacket);
275 }
276
277 if (fclose(fp)<0)
278 eventlog(eventlog_level_error,__FUNCTION__,"could not close file \"%s\" after reading (fclose: %s)",rawname,pstrerror(errno));
279 return 0;
280 }
281