|
1
|
/* |
|
2
|
** Copyright (c) 2006 D. Richard Hipp |
|
3
|
** |
|
4
|
** This program is free software; you can redistribute it and/or |
|
5
|
** modify it under the terms of the Simplified BSD License (also |
|
6
|
** known as the "2-Clause License" or "FreeBSD License".) |
|
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. |
|
11
|
** |
|
12
|
** Author contact information: |
|
13
|
** [email protected] |
|
14
|
** http://www.hwaci.com/drh/ |
|
15
|
** |
|
16
|
******************************************************************************* |
|
17
|
** |
|
18
|
** This module contains code to implement CGI, HTTP, SCGI, and FastCGI |
|
19
|
** servers. |
|
20
|
*/ |
|
21
|
#include "config.h" |
|
22
|
#include "server.h" |
|
23
|
#include <sys/types.h> |
|
24
|
#include <sys/stat.h> |
|
25
|
#if defined(_WIN32) |
|
26
|
# include <windows.h> |
|
27
|
#else |
|
28
|
# include <errno.h> /* errno global */ |
|
29
|
#endif |
|
30
|
|
|
31
|
/* |
|
32
|
** If g.argv[2] exists then it is either the name of a repository |
|
33
|
** that will be used by a server, or else it is a directory that |
|
34
|
** contains multiple repositories that can be served. If g.argv[2] |
|
35
|
** is a directory, the repositories it contains must be named |
|
36
|
** "*.fossil". If g.argv[2] does not exists, then we must be within |
|
37
|
** a check-out and the repository to be served is the repository of |
|
38
|
** that check-out. |
|
39
|
** |
|
40
|
** Open the repository to be served if it is known. If g.argv[2] is |
|
41
|
** a directory full of repositories, then set g.zRepositoryName to |
|
42
|
** the name of that directory and the specific repository will be |
|
43
|
** opened later by process_one_web_page() based on the content of |
|
44
|
** the PATH_INFO variable. |
|
45
|
** |
|
46
|
** If disallowDir is set, then the directory full of repositories method |
|
47
|
** is disallowed. |
|
48
|
*/ |
|
49
|
static void find_server_repository(int disallowDir){ |
|
50
|
if( g.argc<3 ){ |
|
51
|
db_must_be_within_tree(); |
|
52
|
}else if( file_isdir(g.argv[2])==1 ){ |
|
53
|
if( disallowDir ){ |
|
54
|
fossil_fatal("\"%s\" is a directory, not a repository file", g.argv[2]); |
|
55
|
}else{ |
|
56
|
g.zRepositoryName = mprintf("%s", g.argv[2]); |
|
57
|
file_simplify_name(g.zRepositoryName, -1, 0); |
|
58
|
} |
|
59
|
}else{ |
|
60
|
db_open_repository(g.argv[2]); |
|
61
|
} |
|
62
|
} |
|
63
|
|
|
64
|
#if !defined(_WIN32) |
|
65
|
#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__) |
|
66
|
/* |
|
67
|
** Search for an executable on the PATH environment variable. |
|
68
|
** Return true (1) if found and false (0) if not found. |
|
69
|
*/ |
|
70
|
static int binaryOnPath(const char *zBinary){ |
|
71
|
const char *zPath = fossil_getenv("PATH"); |
|
72
|
char *zFull; |
|
73
|
int i; |
|
74
|
int bExists; |
|
75
|
while( zPath && zPath[0] ){ |
|
76
|
while( zPath[0]==':' ) zPath++; |
|
77
|
for(i=0; zPath[i] && zPath[i]!=':'; i++){} |
|
78
|
zFull = mprintf("%.*s/%s", i, zPath, zBinary); |
|
79
|
bExists = file_access(zFull, X_OK); |
|
80
|
fossil_free(zFull); |
|
81
|
if( bExists==0 ) return 1; |
|
82
|
zPath += i; |
|
83
|
} |
|
84
|
return 0; |
|
85
|
} |
|
86
|
#endif |
|
87
|
#endif |
|
88
|
|
|
89
|
/* |
|
90
|
** If running as root, chroot to the directory containing the |
|
91
|
** repository zRepo and then drop root privileges. Return the |
|
92
|
** new repository name. |
|
93
|
** |
|
94
|
** zRepo might be a directory itself. In that case chroot into |
|
95
|
** the directory zRepo. |
|
96
|
** |
|
97
|
** Assume the user-id and group-id of the repository, or if zRepo |
|
98
|
** is a directory, of that directory. |
|
99
|
*/ |
|
100
|
char *enter_chroot_jail(char *zRepo){ |
|
101
|
#if !defined(_WIN32) |
|
102
|
if( getuid()==0 ){ |
|
103
|
int i; |
|
104
|
struct stat sStat; |
|
105
|
Blob dir; |
|
106
|
char *zDir; |
|
107
|
|
|
108
|
file_canonical_name(zRepo, &dir, 0); |
|
109
|
zDir = blob_str(&dir); |
|
110
|
if( file_isdir(zDir)==1 ){ |
|
111
|
if( file_chdir(zDir, 1) ){ |
|
112
|
fossil_fatal("unable to chroot into %s", zDir); |
|
113
|
} |
|
114
|
zRepo = "/"; |
|
115
|
}else{ |
|
116
|
for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){} |
|
117
|
if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo); |
|
118
|
if( i>0 ){ |
|
119
|
zDir[i] = 0; |
|
120
|
if( file_chdir(zDir, 1) ){ |
|
121
|
fossil_fatal("unable to chroot into %s", zDir); |
|
122
|
} |
|
123
|
zDir[i] = '/'; |
|
124
|
} |
|
125
|
zRepo = &zDir[i]; |
|
126
|
} |
|
127
|
if( stat(zRepo, &sStat)!=0 ){ |
|
128
|
fossil_fatal("cannot stat() repository: %s", zRepo); |
|
129
|
} |
|
130
|
i = setgid(sStat.st_gid); |
|
131
|
i = i || setuid(sStat.st_uid); |
|
132
|
if(i){ |
|
133
|
fossil_fatal("setgid/uid() failed with errno %d", errno); |
|
134
|
} |
|
135
|
if( g.db!=0 ){ |
|
136
|
db_close(1); |
|
137
|
db_open_repository(zRepo); |
|
138
|
} |
|
139
|
} |
|
140
|
#endif |
|
141
|
return zRepo; |
|
142
|
} |
|
143
|
|
|
144
|
/* If the CGI program contains one or more lines of the form |
|
145
|
** |
|
146
|
** redirect: repository-filename http://hostname/path/%s |
|
147
|
** |
|
148
|
** then control jumps here. Search each repository for an artifact ID |
|
149
|
** that matches the "name" CGI parameter and for the first match, |
|
150
|
** redirect to the corresponding URL with the "name" CGI parameter |
|
151
|
** inserted. Paint an error page if no match is found. |
|
152
|
** |
|
153
|
** If there is a line of the form: |
|
154
|
** |
|
155
|
** redirect: * URL |
|
156
|
** |
|
157
|
** Then a redirect is made to URL if no match is found. Otherwise a |
|
158
|
** very primitive error message is returned. |
|
159
|
*/ |
|
160
|
static void redirect_web_page(int nRedirect, char **azRedirect){ |
|
161
|
int i; /* Loop counter */ |
|
162
|
const char *zNotFound = 0; /* Not found URL */ |
|
163
|
const char *zName = P("name"); |
|
164
|
set_base_url(0); |
|
165
|
if( zName==0 ){ |
|
166
|
zName = P("SCRIPT_NAME"); |
|
167
|
if( zName && zName[0]=='/' ) zName++; |
|
168
|
} |
|
169
|
if( zName && validate16(zName, strlen(zName)) ){ |
|
170
|
for(i=0; i<nRedirect; i++){ |
|
171
|
if( fossil_strcmp(azRedirect[i*2],"*")==0 ){ |
|
172
|
zNotFound = azRedirect[i*2+1]; |
|
173
|
continue; |
|
174
|
} |
|
175
|
db_open_repository(azRedirect[i*2]); |
|
176
|
if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%s*'", zName) ){ |
|
177
|
cgi_redirectf(azRedirect[i*2+1], zName); |
|
178
|
return; |
|
179
|
} |
|
180
|
db_close(1); |
|
181
|
} |
|
182
|
} |
|
183
|
if( zNotFound ){ |
|
184
|
cgi_redirectf(zNotFound, zName); |
|
185
|
}else{ |
|
186
|
@ <html> |
|
187
|
@ <head><title>No Such Object</title></head> |
|
188
|
@ <body> |
|
189
|
@ <p>No such object: <b>%h(zName)</b></p> |
|
190
|
@ </body> |
|
191
|
cgi_reply(); |
|
192
|
} |
|
193
|
} |
|
194
|
|
|
195
|
/* |
|
196
|
** COMMAND: cgi* |
|
197
|
** |
|
198
|
** Usage: %fossil ?cgi? SCRIPT |
|
199
|
** |
|
200
|
** The SCRIPT argument is the name of a file that is the CGI script |
|
201
|
** that is being run. The command name, "cgi", may be omitted if |
|
202
|
** the GATEWAY_INTERFACE environment variable is set to "CGI" (which |
|
203
|
** should always be the case for CGI scripts run by a webserver.) The |
|
204
|
** SCRIPT file should look something like this: |
|
205
|
** |
|
206
|
** #!/usr/bin/fossil |
|
207
|
** repository: /home/somebody/project.db |
|
208
|
** |
|
209
|
** The second line defines the name of the repository. After locating |
|
210
|
** the repository, fossil will generate a webpage on stdout based on |
|
211
|
** the values of standard CGI environment variables. |
|
212
|
** |
|
213
|
** See also: http, server, winsrv |
|
214
|
*/ |
|
215
|
void cmd_cgi(void){ |
|
216
|
const char *zFile; |
|
217
|
const char *zNotFound = 0; |
|
218
|
char **azRedirect = 0; /* List of repositories to redirect to */ |
|
219
|
int nRedirect = 0; /* Number of entries in azRedirect */ |
|
220
|
Glob *pFileGlob = 0; /* Pattern for files */ |
|
221
|
Blob config, line, key, value, value2; |
|
222
|
if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){ |
|
223
|
zFile = g.argv[2]; |
|
224
|
}else{ |
|
225
|
zFile = g.argv[1]; |
|
226
|
} |
|
227
|
g.httpOut = stdout; |
|
228
|
g.httpIn = stdin; |
|
229
|
fossil_binary_mode(g.httpOut); |
|
230
|
fossil_binary_mode(g.httpIn); |
|
231
|
g.cgiOutput = 1; |
|
232
|
blob_read_from_file(&config, zFile); |
|
233
|
while( blob_line(&config, &line) ){ |
|
234
|
if( !blob_token(&line, &key) ) continue; |
|
235
|
if( blob_buffer(&key)[0]=='#' ) continue; |
|
236
|
if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){ |
|
237
|
g.fDebug = fossil_fopen(blob_str(&value), "ab"); |
|
238
|
blob_reset(&value); |
|
239
|
continue; |
|
240
|
} |
|
241
|
if( blob_eq(&key, "HOME:") && blob_token(&line, &value) ){ |
|
242
|
cgi_setenv("HOME", blob_str(&value)); |
|
243
|
blob_reset(&value); |
|
244
|
continue; |
|
245
|
} |
|
246
|
if( blob_eq(&key, "repository:") && blob_tail(&line, &value) ){ |
|
247
|
blob_trim(&value); |
|
248
|
db_open_repository(blob_str(&value)); |
|
249
|
blob_reset(&value); |
|
250
|
continue; |
|
251
|
} |
|
252
|
if( blob_eq(&key, "directory:") && blob_token(&line, &value) ){ |
|
253
|
db_close(1); |
|
254
|
g.zRepositoryName = mprintf("%s", blob_str(&value)); |
|
255
|
blob_reset(&value); |
|
256
|
continue; |
|
257
|
} |
|
258
|
if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){ |
|
259
|
zNotFound = mprintf("%s", blob_str(&value)); |
|
260
|
blob_reset(&value); |
|
261
|
continue; |
|
262
|
} |
|
263
|
if( blob_eq(&key, "localauth") ){ |
|
264
|
g.useLocalauth = 1; |
|
265
|
continue; |
|
266
|
} |
|
267
|
if( blob_eq(&key, "redirect:") && blob_token(&line, &value) |
|
268
|
&& blob_token(&line, &value2) ){ |
|
269
|
nRedirect++; |
|
270
|
azRedirect = fossil_realloc(azRedirect, 2*nRedirect*sizeof(char*)); |
|
271
|
azRedirect[nRedirect*2-2] = mprintf("%s", blob_str(&value)); |
|
272
|
azRedirect[nRedirect*2-1] = mprintf("%s", blob_str(&value2)); |
|
273
|
blob_reset(&value); |
|
274
|
blob_reset(&value2); |
|
275
|
continue; |
|
276
|
} |
|
277
|
if( blob_eq(&key, "files:") && blob_token(&line, &value) ){ |
|
278
|
pFileGlob = glob_create(blob_str(&value)); |
|
279
|
continue; |
|
280
|
} |
|
281
|
} |
|
282
|
blob_reset(&config); |
|
283
|
if( g.db==0 && g.zRepositoryName==0 && nRedirect==0 ){ |
|
284
|
cgi_panic("Unable to find or open the project repository"); |
|
285
|
} |
|
286
|
cgi_init(); |
|
287
|
if( nRedirect ){ |
|
288
|
redirect_web_page(nRedirect, azRedirect); |
|
289
|
}else{ |
|
290
|
process_one_web_page(zNotFound, pFileGlob); |
|
291
|
} |
|
292
|
} |
|
293
|
|
|
294
|
|
|
295
|
/* |
|
296
|
** undocumented format: |
|
297
|
** |
|
298
|
** fossil http REPOSITORY INFILE OUTFILE IPADDR |
|
299
|
** |
|
300
|
** The argv==6 form is used by the win32 server only. |
|
301
|
** |
|
302
|
** COMMAND: http* |
|
303
|
** |
|
304
|
** Usage: %fossil http REPOSITORY ?OPTIONS? |
|
305
|
** |
|
306
|
** Handle a single HTTP request appearing on stdin. The resulting webpage |
|
307
|
** is delivered on stdout. This method is used to launch an HTTP request |
|
308
|
** handler from inetd, for example. The argument is the name of the |
|
309
|
** repository. |
|
310
|
** |
|
311
|
** If REPOSITORY is a directory that contains one or more repositories, |
|
312
|
** either directly in REPOSITORY itself, or in subdirectories, and |
|
313
|
** with names of the form "*.fossil" then the a prefix of the URL pathname |
|
314
|
** selects from among the various repositories. If the pathname does |
|
315
|
** not select a valid repository and the --notfound option is available, |
|
316
|
** then the server redirects (HTTP code 302) to the URL of --notfound. |
|
317
|
** When REPOSITORY is a directory, the pathname must contain only |
|
318
|
** alphanumerics, "_", "/", "-" and "." and no "-" may occur after a "/" |
|
319
|
** and every "." must be surrounded on both sides by alphanumerics or else |
|
320
|
** a 404 error is returned. Static content files in the directory are |
|
321
|
** returned if they match comma-separate GLOB pattern specified by --files |
|
322
|
** and do not match "*.fossil*" and have a well-known suffix. |
|
323
|
** |
|
324
|
** The --host option can be used to specify the hostname for the server. |
|
325
|
** The --https option indicates that the request came from HTTPS rather |
|
326
|
** than HTTP. If --nossl is given, then SSL connections will not be available, |
|
327
|
** thus also no redirecting from http: to https: will take place. |
|
328
|
** |
|
329
|
** If the --localauth option is given, then automatic login is performed |
|
330
|
** for requests coming from localhost, if the "localauth" setting is not |
|
331
|
** enabled. |
|
332
|
** |
|
333
|
** Options: |
|
334
|
** --localauth enable automatic login for local connections |
|
335
|
** --host NAME specify hostname of the server |
|
336
|
** --https signal a request coming in via https |
|
337
|
** --nossl signal that no SSL connections are available |
|
338
|
** --notfound URL use URL as "HTTP 404, object not found" page. |
|
339
|
** --files GLOB comma-separate glob patterns for static file to serve |
|
340
|
** --baseurl URL base URL (useful with reverse proxies) |
|
341
|
** |
|
342
|
** See also: cgi, server, winsrv |
|
343
|
*/ |
|
344
|
void cmd_http(void){ |
|
345
|
const char *zIpAddr; |
|
346
|
const char *zNotFound; |
|
347
|
const char *zHost; |
|
348
|
const char *zAltBase; |
|
349
|
const char *zFileGlob; |
|
350
|
|
|
351
|
/* The winhttp module passes the --files option as --files-urlenc with |
|
352
|
** the argument being URL encoded, to avoid wildcard expansion in the |
|
353
|
** shell. This option is for internal use and is undocumented. |
|
354
|
*/ |
|
355
|
zFileGlob = find_option("files-urlenc",0,1); |
|
356
|
if( zFileGlob ){ |
|
357
|
char *z = mprintf("%s", zFileGlob); |
|
358
|
dehttpize(z); |
|
359
|
zFileGlob = z; |
|
360
|
}else{ |
|
361
|
zFileGlob = find_option("files",0,1); |
|
362
|
} |
|
363
|
zNotFound = find_option("notfound", 0, 1); |
|
364
|
g.useLocalauth = find_option("localauth", 0, 0)!=0; |
|
365
|
g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
|
366
|
zAltBase = find_option("baseurl", 0, 1); |
|
367
|
if( zAltBase ) set_base_url(zAltBase); |
|
368
|
if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on"); |
|
369
|
zHost = find_option("host", 0, 1); |
|
370
|
if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
|
371
|
g.cgiOutput = 1; |
|
372
|
if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ |
|
373
|
fossil_fatal("no repository specified"); |
|
374
|
} |
|
375
|
g.fullHttpReply = 1; |
|
376
|
if( g.argc==6 ){ |
|
377
|
g.httpIn = fossil_fopen(g.argv[3], "rb"); |
|
378
|
g.httpOut = fossil_fopen(g.argv[4], "wb"); |
|
379
|
zIpAddr = g.argv[5]; |
|
380
|
}else{ |
|
381
|
g.httpIn = stdin; |
|
382
|
g.httpOut = stdout; |
|
383
|
zIpAddr = 0; |
|
384
|
} |
|
385
|
find_server_repository(0); |
|
386
|
g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
|
387
|
cgi_handle_http_request(zIpAddr); |
|
388
|
process_one_web_page(zNotFound, glob_create(zFileGlob)); |
|
389
|
} |
|
390
|
|
|
391
|
/* |
|
392
|
** Note that the following command is used by ssh:// processing. |
|
393
|
** |
|
394
|
** COMMAND: test-http |
|
395
|
** Works like the http command but gives setup permission to all users. |
|
396
|
*/ |
|
397
|
void cmd_test_http(void){ |
|
398
|
Th_InitTraceLog(); |
|
399
|
login_set_capabilities("sx", 0); |
|
400
|
g.useLocalauth = 1; |
|
401
|
cgi_set_parameter("REMOTE_ADDR", "127.0.0.1"); |
|
402
|
g.httpIn = stdin; |
|
403
|
g.httpOut = stdout; |
|
404
|
find_server_repository(0); |
|
405
|
g.cgiOutput = 1; |
|
406
|
g.fullHttpReply = 1; |
|
407
|
cgi_handle_http_request(0); |
|
408
|
process_one_web_page(0, 0); |
|
409
|
} |
|
410
|
|
|
411
|
|
|
412
|
/* |
|
413
|
** COMMAND: server* |
|
414
|
** COMMAND: ui |
|
415
|
** |
|
416
|
** Usage: %fossil server ?OPTIONS? ?REPOSITORY? |
|
417
|
** Or: %fossil ui ?OPTIONS? ?REPOSITORY? |
|
418
|
** |
|
419
|
** Open a socket and begin listening and responding to HTTP requests on |
|
420
|
** TCP port 8080, or on any other TCP port defined by the -P or |
|
421
|
** --port option. The optional argument is the name of the repository. |
|
422
|
** The repository argument may be omitted if the working directory is |
|
423
|
** within an open checkout. |
|
424
|
** |
|
425
|
** The "ui" command automatically starts a web browser after initializing |
|
426
|
** the web server. The "ui" command also binds to 127.0.0.1 and so will |
|
427
|
** only process HTTP traffic from the local machine. |
|
428
|
** |
|
429
|
** The REPOSITORY can be a directory (aka folder) that contains one or |
|
430
|
** more repositories with names ending in ".fossil". In this case, the |
|
431
|
** a prefix of the URL pathname is used to search the directory for an |
|
432
|
** appropriate repository. To thwart mischief, the pathname in the URL must |
|
433
|
** contain only alphanumerics, "_", "/", "-", and ".", and no "-" may |
|
434
|
** occur after "/", and every "." must be surrounded on both sides by |
|
435
|
** alphanumerics. Any pathname that does not satisfy these constraints |
|
436
|
** results in a 404 error. Files in REPOSITORY that match the comma-separated |
|
437
|
** list of glob patterns given by --files and that have known suffixes |
|
438
|
** such as ".txt" or ".html" or ".jpeg" and do not match the pattern |
|
439
|
** "*.fossil*" will be served as static content. With the "ui" command, |
|
440
|
** the REPOSITORY can only be a directory if the --notfound option is |
|
441
|
** also present. |
|
442
|
** |
|
443
|
** By default, the "ui" command provides full administrative access without |
|
444
|
** having to log in. This can be disabled by setting turning off the |
|
445
|
** "localauth" setting. Automatic login for the "server" command is available |
|
446
|
** if the --localauth option is present and the "localauth" setting is off |
|
447
|
** and the connection is from localhost. The optional REPOSITORY argument |
|
448
|
** to "ui" may be a directory and will function as "server" if and only if |
|
449
|
** the --notfound option is used. |
|
450
|
** |
|
451
|
** Options: |
|
452
|
** --localauth enable automatic login for requests from localhost |
|
453
|
** --localhost listen on 127.0.0.1 only (always true for "ui") |
|
454
|
** -P|--port TCPPORT listen to request on port TCPPORT |
|
455
|
** --th-trace trace TH1 execution (for debugging purposes) |
|
456
|
** --baseurl URL Use URL as the base (useful for reverse proxies) |
|
457
|
** --notfound URL Redirect |
|
458
|
** --files GLOBLIST Comma-separated list of glob patterns for static files |
|
459
|
** |
|
460
|
** See also: cgi, http, winsrv |
|
461
|
*/ |
|
462
|
void cmd_webserver(void){ |
|
463
|
int iPort, mxPort; /* Range of TCP ports allowed */ |
|
464
|
const char *zPort; /* Value of the --port option */ |
|
465
|
const char *zBrowser; /* Name of web browser program */ |
|
466
|
char *zBrowserCmd = 0; /* Command to launch the web browser */ |
|
467
|
int isUiCmd; /* True if command is "ui", not "server' */ |
|
468
|
const char *zNotFound; /* The --notfound option or NULL */ |
|
469
|
int flags = 0; /* Server flags */ |
|
470
|
const char *zAltBase; /* Argument to the --baseurl option */ |
|
471
|
const char *zFileGlob; /* Static content must match this */ |
|
472
|
char *zIpAddr = 0; /* Bind to this IP address */ |
|
473
|
|
|
474
|
#if defined(_WIN32) |
|
475
|
const char *zStopperFile; /* Name of file used to terminate server */ |
|
476
|
zStopperFile = find_option("stopper", 0, 1); |
|
477
|
#endif |
|
478
|
|
|
479
|
zFileGlob = find_option("files", 0, 1); |
|
480
|
g.useLocalauth = find_option("localauth", 0, 0)!=0; |
|
481
|
Th_InitTraceLog(); |
|
482
|
zPort = find_option("port", "P", 1); |
|
483
|
zNotFound = find_option("notfound", 0, 1); |
|
484
|
zAltBase = find_option("baseurl", 0, 1); |
|
485
|
if( zAltBase ){ |
|
486
|
set_base_url(zAltBase); |
|
487
|
} |
|
488
|
if ( find_option("localhost", 0, 0)!=0 ){ |
|
489
|
flags |= HTTP_SERVER_LOCALHOST; |
|
490
|
} |
|
491
|
if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); |
|
492
|
isUiCmd = g.argv[1][0]=='u'; |
|
493
|
if( isUiCmd ){ |
|
494
|
flags |= HTTP_SERVER_LOCALHOST; |
|
495
|
g.useLocalauth = 1; |
|
496
|
} |
|
497
|
find_server_repository(isUiCmd && zNotFound==0); |
|
498
|
if( zPort ){ |
|
499
|
int i; |
|
500
|
for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){} |
|
501
|
if( i>0 ){ |
|
502
|
zIpAddr = mprintf("%.*s", i, zPort); |
|
503
|
zPort += i+1; |
|
504
|
} |
|
505
|
iPort = mxPort = atoi(zPort); |
|
506
|
}else{ |
|
507
|
iPort = db_get_int("http-port", 8080); |
|
508
|
mxPort = iPort+100; |
|
509
|
} |
|
510
|
#if !defined(_WIN32) |
|
511
|
/* Unix implementation */ |
|
512
|
if( isUiCmd ){ |
|
513
|
#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__) |
|
514
|
zBrowser = db_get("web-browser", 0); |
|
515
|
if( zBrowser==0 ){ |
|
516
|
static const char *const azBrowserProg[] = |
|
517
|
{ "xdg-open", "gnome-open", "firefox", "google-chrome" }; |
|
518
|
int i; |
|
519
|
zBrowser = "echo"; |
|
520
|
for(i=0; i<sizeof(azBrowserProg)/sizeof(azBrowserProg[0]); i++){ |
|
521
|
if( binaryOnPath(azBrowserProg[i]) ){ |
|
522
|
zBrowser = azBrowserProg[i]; |
|
523
|
break; |
|
524
|
} |
|
525
|
} |
|
526
|
} |
|
527
|
#else |
|
528
|
zBrowser = db_get("web-browser", "open"); |
|
529
|
#endif |
|
530
|
if( zIpAddr ){ |
|
531
|
zBrowserCmd = mprintf("%s http://%s:%%d/ &", zBrowser, zIpAddr); |
|
532
|
}else{ |
|
533
|
zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser); |
|
534
|
} |
|
535
|
} |
|
536
|
db_close(1); |
|
537
|
if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){ |
|
538
|
fossil_fatal("unable to listen on TCP socket %d", iPort); |
|
539
|
} |
|
540
|
g.sslNotAvailable = 1; |
|
541
|
g.httpIn = stdin; |
|
542
|
g.httpOut = stdout; |
|
543
|
if( g.fHttpTrace || g.fSqlTrace ){ |
|
544
|
fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
|
545
|
} |
|
546
|
g.cgiOutput = 1; |
|
547
|
find_server_repository(isUiCmd && zNotFound==0); |
|
548
|
g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
|
549
|
cgi_handle_http_request(0); |
|
550
|
process_one_web_page(zNotFound, glob_create(zFileGlob)); |
|
551
|
#else |
|
552
|
/* Win32 implementation */ |
|
553
|
if( isUiCmd ){ |
|
554
|
zBrowser = db_get("web-browser", "start"); |
|
555
|
if( zIpAddr ){ |
|
556
|
zBrowserCmd = mprintf("%s http://%s:%%d/ &", zBrowser, zIpAddr); |
|
557
|
}else{ |
|
558
|
zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser); |
|
559
|
} |
|
560
|
} |
|
561
|
db_close(1); |
|
562
|
if( win32_http_service(iPort, zNotFound, zFileGlob, flags) ){ |
|
563
|
win32_http_server(iPort, mxPort, zBrowserCmd, |
|
564
|
zStopperFile, zNotFound, zFileGlob, zIpAddr, flags); |
|
565
|
} |
|
566
|
#endif |
|
567
|
} |
|
568
|
|