1 /*
2 * $LynxId: HTRules.c,v 1.44 2013/05/03 09:42:04 tom Exp $
3 *
4 * Configuration manager for Hypertext Daemon HTRules.c
5 * ==========================================
6 *
7 *
8 * History:
9 * 3 Jun 91 Written TBL
10 * 10 Aug 91 Authorisation added after Daniel Martin (pass, fail)
11 * Rule order in file changed
12 * Comments allowed with # on 1st char of rule line
13 * 17 Jun 92 Bug fix: pass and fail failed if didn't contain '*' TBL
14 * 1 Sep 93 Bug fix: no memory check - Nathan Torkington
15 * BYTE_ADDRESSING removed - Arthur Secret
16 * 11 Sep 93 MD Changed %i into %d in debug printf.
17 * VMS does not recognize %i.
18 * Bug Fix: in case of PASS, only one parameter to printf.
19 * 19 Sep 93 AL Added Access Authorization stuff.
20 * 1 Nov 93 AL Added htbin.
21 * 25 May 99 KW Added redirect for lynx.
22 *
23 */
24
25 #include <HTUtils.h>
26
27 /* (c) CERN WorldWideWeb project 1990,91. See Copyright.html for details */
28 #include <HTRules.h>
29
30 #include <HTFile.h>
31 #include <LYLeaks.h>
32 #include <HTAAProt.h>
33
34 #define LINE_LENGTH 256
35
36 typedef struct _rule {
37 struct _rule *next;
38 HTRuleOp op;
39 char *pattern;
40 char *equiv;
41 char *condition_op; /* as strings - may be inefficient, */
42 char *condition; /* but this is not for a server - kw */
43 } rule;
44
45 #ifndef NO_RULES
46
47 #include <HTTP.h> /* for redirecting_url, indirectly HTPermitRedir - kw */
48 #include <LYGlobalDefs.h> /* for LYUserSpecifiedURL - kw */
49 #include <LYStrings.h> /* for LYscanFloat */
50 #include <LYUtils.h> /* for LYFixCursesOn - kw */
51 #include <HTAlert.h>
52
53 /* Global variables
54 * ----------------
55 */
56 char *HTBinDir = NULL; /* Physical /htbin directory path. */
57
58 /* In future this should not be global. */
59 char *HTSearchScript = NULL; /* Search script name. */
60
61 /* Module-wide variables
62 * ---------------------
63 */
64
65 static rule *rules = 0; /* Pointer to first on list */
66
67 #ifndef PUT_ON_HEAD
68 static rule *rule_tail = 0; /* Pointer to last on list */
69 #endif
70
71 /* Add rule to the list HTAddRule()
72 * --------------------
73 *
74 * On entry,
75 * pattern points to 0-terminated string containing a single "*"
76 * equiv points to the equivalent string with * for the
77 * place where the text matched by * goes.
78 * On exit,
79 * returns 0 if success, -1 if error.
80 */
81
HTAddRule(HTRuleOp op,const char * pattern,const char * equiv,const char * cond_op,const char * cond)82 int HTAddRule(HTRuleOp op, const char *pattern,
83 const char *equiv,
84 const char *cond_op,
85 const char *cond)
86 { /* BYTE_ADDRESSING removed and memory check - AS - 1 Sep 93 */
87 rule *temp;
88 char *pPattern = NULL;
89
90 temp = typecalloc(rule);
91 if (temp == NULL)
92 outofmem(__FILE__, "HTAddRule");
93
94 assert(temp != NULL);
95
96 if (equiv) { /* Two operands */
97 char *pEquiv = NULL;
98
99 StrAllocCopy(pEquiv, equiv);
100 temp->equiv = pEquiv;
101 } else {
102 temp->equiv = 0;
103 }
104 if (cond_op) {
105 StrAllocCopy(temp->condition_op, cond_op);
106 StrAllocCopy(temp->condition, cond);
107 }
108 StrAllocCopy(pPattern, pattern);
109 temp->pattern = pPattern;
110 temp->op = op;
111
112 if (equiv) {
113 CTRACE((tfp, "Rule: For `%s' op %d `%s'", pattern, (int) op, equiv));
114 } else {
115 CTRACE((tfp, "Rule: For `%s' op %d", pattern, (int) op));
116 }
117 if (cond_op) {
118 CTRACE((tfp, "\t%s %s\n", cond_op, NONNULL(cond)));
119 } else {
120 CTRACE((tfp, "\n"));
121 }
122
123 if (!rules) {
124 #ifdef LY_FIND_LEAKS
125 atexit(HTClearRules);
126 #endif
127 }
128 #ifdef PUT_ON_HEAD
129 temp->next = rules;
130 rules = temp;
131 #else
132 temp->next = 0;
133 if (rule_tail)
134 rule_tail->next = temp;
135 else
136 rules = temp;
137 rule_tail = temp;
138 #endif
139
140 return 0;
141 }
142
143 /* Clear all rules HTClearRules()
144 * ---------------
145 *
146 * On exit,
147 * There are no rules
148 *
149 * See also
150 * HTAddRule()
151 */
HTClearRules(void)152 void HTClearRules(void)
153 {
154 while (rules) {
155 rule *temp = rules;
156
157 rules = temp->next;
158 FREE(temp->pattern);
159 FREE(temp->equiv);
160 FREE(temp->condition_op);
161 FREE(temp->condition);
162 FREE(temp);
163 }
164 #ifndef PUT_ON_HEAD
165 rule_tail = 0;
166 #endif
167 }
168
rule_cond_ok(rule * r)169 static BOOL rule_cond_ok(rule * r)
170 {
171 BOOL result;
172
173 if (!r->condition_op)
174 return YES;
175 if (strcmp(r->condition_op, "if") && strcmp(r->condition_op, "unless")) {
176 CTRACE((tfp, "....... rule ignored, unrecognized `%s'!\n",
177 r->condition_op));
178 return NO;
179 }
180 if (!strcmp(r->condition, "redirected"))
181 result = (BOOL) (redirection_attempts > 0);
182 else if (!strcmp(r->condition, "userspec"))
183 result = LYUserSpecifiedURL;
184 else {
185 CTRACE((tfp, "....... rule ignored, unrecognized `%s %s'!\n",
186 r->condition_op, NONNULL(r->condition)));
187 return NO;
188 }
189 if (!strcmp(r->condition_op, "if"))
190 return result;
191 else
192 return (BOOL) (!result);
193
194 }
195
196 /* Translate by rules HTTranslate()
197 * ------------------
198 *
199 * The most recently defined rules are applied first.
200 *
201 * On entry,
202 * required points to a string whose equivalent value is needed
203 * On exit,
204 * returns the address of the equivalent string allocated from
205 * the heap which the CALLER MUST FREE. If no translation
206 * occurred, then it is a copy of the original.
207 * NEW FEATURES:
208 * When a "protect" or "defprot" rule is matched,
209 * a call to HTAA_setCurrentProtection() or
210 * HTAA_setDefaultProtection() is made to notify
211 * the Access Authorization module that the file is
212 * protected, and so it knows how to handle it.
213 * -- AL
214 */
HTTranslate(const char * required)215 char *HTTranslate(const char *required)
216 {
217 rule *r;
218 char *current = NULL;
219 char *msgtmp = NULL;
220 const char *pMsg;
221 int proxy_none_flag = 0;
222 int permitredir_flag = 0;
223
224 StrAllocCopy(current, required);
225
226 HTAA_clearProtections(); /* Reset from previous call -- AL */
227
228 for (r = rules; r; r = r->next) {
229 char *p = r->pattern;
230 int m = 0; /* Number of characters matched against wildcard */
231 const char *q = current;
232
233 for (; *p && *q; p++, q++) { /* Find first mismatch */
234 if (*p != *q)
235 break;
236 }
237
238 if (*p == '*') { /* Match up to wildcard */
239 m = (int) strlen(q) - (int) strlen(p + 1); /* Amount to match to wildcard */
240 if (m < 0)
241 continue; /* tail is too short to match */
242 if (0 != strcmp(q + m, p + 1))
243 continue; /* Tail mismatch */
244 } else
245 /* Not wildcard */ if (*p != *q)
246 continue; /* plain mismatch: go to next rule */
247
248 if (!rule_cond_ok(r)) /* check condition, next rule if false - kw */
249 continue;
250
251 switch (r->op) { /* Perform operation */
252
253 case HT_DefProt:
254 case HT_Protect:
255 #ifdef ACCESS_AUTH
256 {
257 char *local_copy = NULL;
258 char *p2;
259 char *eff_ids = NULL;
260 char *prot_file = NULL;
261
262 CTRACE((tfp, "HTRule: `%s' matched %s %s: `%s'\n",
263 current,
264 (r->op == HT_Protect ? "Protect" : "DefProt"),
265 "rule, setup",
266 (r->equiv ? r->equiv :
267 (r->op == HT_Protect ? "DEFAULT" : "NULL!!"))));
268
269 if (r->equiv) {
270 StrAllocCopy(local_copy, r->equiv);
271 p2 = local_copy;
272 prot_file = HTNextField(&p2);
273 eff_ids = HTNextField(&p2);
274 }
275
276 if (r->op == HT_Protect)
277 HTAA_setCurrentProtection(current, prot_file, eff_ids);
278 else
279 HTAA_setDefaultProtection(current, prot_file, eff_ids);
280
281 FREE(local_copy);
282
283 /* continue translating rules */
284 }
285 #endif /* ACCESS_AUTH */
286 break;
287
288 case HT_UserMsg: /* Produce message immediately */
289 LYFixCursesOn("show rule message:");
290 HTUserMsg2((r->equiv ? r->equiv : "Rule: %s"), current);
291 break;
292 case HT_InfoMsg: /* Produce messages immediately */
293 case HT_Progress:
294 case HT_Alert:
295 LYFixCursesOn("show rule message:"); /* and fall through */
296 case HT_AlwaysAlert:
297 pMsg = r->equiv ? r->equiv :
298 (r->op == HT_AlwaysAlert) ? "%s" : "Rule: %s";
299 if (strchr(pMsg, '%')) {
300 HTSprintf0(&msgtmp, pMsg, current);
301 pMsg = msgtmp;
302 }
303 switch (r->op) { /* Actually produce message */
304 case HT_InfoMsg:
305 HTInfoMsg(pMsg);
306 break;
307 case HT_Progress:
308 HTProgress(pMsg);
309 break;
310 case HT_Alert:
311 HTAlert(pMsg);
312 break;
313 case HT_AlwaysAlert:
314 HTAlwaysAlert("Rule alert:", pMsg);
315 break;
316 default:
317 break;
318 }
319 FREE(msgtmp);
320 break;
321
322 case HT_PermitRedir: /* Set special flag */
323 permitredir_flag = 1;
324 CTRACE((tfp, "HTRule: Mark for redirection permitted\n"));
325 break;
326
327 case HT_Pass: /* Authorised */
328 if (!r->equiv) {
329 if (proxy_none_flag) {
330 char *temp = NULL;
331
332 StrAllocCopy(temp, "NoProxy=");
333 StrAllocCat(temp, current);
334 FREE(current);
335 current = temp;
336 }
337 CTRACE((tfp, "HTRule: Pass `%s'\n", current));
338 return current;
339 }
340 /* Else fall through ...to map and pass */
341
342 case HT_Map:
343 case HT_Redirect:
344 case HT_RedirectPerm:
345 if (*p == *q) { /* End of both strings, no wildcard */
346 CTRACE((tfp, "For `%s' using `%s'\n", current, r->equiv));
347 StrAllocCopy(current, r->equiv); /* use entire translation */
348 } else {
349 char *ins = strchr(r->equiv, '*'); /* Insertion point */
350
351 if (ins) { /* Consistent rule!!! */
352 char *temp = NULL;
353
354 HTSprintf0(&temp, "%.*s%.*s%s",
355 (int) (ins - r->equiv),
356 r->equiv,
357 m,
358 q,
359 ins + 1);
360 CTRACE((tfp, "For `%s' using `%s'\n",
361 current, temp));
362 FREE(current);
363 current = temp; /* Use this */
364
365 } else { /* No insertion point */
366 char *temp = NULL;
367
368 StrAllocCopy(temp, r->equiv);
369 CTRACE((tfp, "For `%s' using `%s'\n",
370 current, temp));
371 FREE(current);
372 current = temp; /* Use this */
373 } /* If no insertion point exists */
374 }
375 if (r->op == HT_Pass) {
376 if (proxy_none_flag) {
377 char *temp = NULL;
378
379 StrAllocCopy(temp, "NoProxy=");
380 StrAllocCat(temp, current);
381 FREE(current);
382 current = temp;
383 }
384 CTRACE((tfp, "HTRule: ...and pass `%s'\n",
385 current));
386 return current;
387 } else if (r->op == HT_Redirect) {
388 CTRACE((tfp, "HTRule: ...and redirect to `%s'\n",
389 current));
390 redirecting_url = current;
391 HTPermitRedir = (BOOL) (permitredir_flag == 1);
392 return (char *) 0;
393 } else if (r->op == HT_RedirectPerm) {
394 CTRACE((tfp, "HTRule: ...and redirect like 301 to `%s'\n",
395 current));
396 redirecting_url = current;
397 permanent_redirection = TRUE;
398 HTPermitRedir = (BOOL) (permitredir_flag == 1);
399 return (char *) 0;
400 }
401 break;
402
403 case HT_UseProxy:
404 if (r->equiv && 0 == strcasecomp(r->equiv, "none")) {
405 CTRACE((tfp, "For `%s' will not use proxy\n", current));
406 proxy_none_flag = 1;
407 } else if (proxy_none_flag) {
408 CTRACE((tfp, "For `%s' proxy server ignored: %s\n",
409 current,
410 NONNULL(r->equiv)));
411 } else {
412 char *temp = NULL;
413
414 StrAllocCopy(temp, "Proxied=");
415 StrAllocCat(temp, r->equiv);
416 StrAllocCat(temp, current);
417 CTRACE((tfp, "HTRule: proxy server found: %s\n",
418 NONNULL(r->equiv)));
419 FREE(current);
420 return temp;
421 }
422 break;
423
424 case HT_Invalid:
425 case HT_Fail: /* Unauthorised */
426 CTRACE((tfp, "HTRule: *** FAIL `%s'\n", current));
427 FREE(current);
428 return (char *) 0;
429 } /* if tail matches ... switch operation */
430
431 } /* loop over rules */
432
433 if (proxy_none_flag) {
434 char *temp = NULL;
435
436 StrAllocCopy(temp, "NoProxy=");
437 StrAllocCat(temp, current);
438 FREE(current);
439 return temp;
440 }
441
442 return current;
443 }
444
445 /* Load one line of configuration
446 * ------------------------------
447 *
448 * Call this, for example, to load a X resource with config info.
449 *
450 * returns 0 OK, < 0 syntax error.
451 */
HTSetConfiguration(char * config)452 int HTSetConfiguration(char *config)
453 {
454 HTRuleOp op;
455 char *line = NULL;
456 char *pointer = NULL;
457 char *word1;
458 const char *word2;
459 const char *word3;
460 const char *cond_op = NULL;
461 const char *cond = NULL;
462 float quality, secs, secs_per_byte;
463 long maxbytes;
464 int status;
465
466 StrAllocCopy(line, config);
467 if (line != NULL) {
468 char *p = line;
469
470 /* Chop off comments */
471 while ((p = strchr(p, '#'))) {
472 if (p == line || isspace(UCH(*(p - 1)))) {
473 *p = 0;
474 break;
475 } else {
476 p++;
477 }
478 }
479 }
480 pointer = line;
481 word1 = HTNextField(&pointer);
482 if (!word1) {
483 FREE(line);
484 return 0;
485 }; /* Comment only or blank */
486
487 word2 = HTNextField(&pointer);
488
489 if (0 == strcasecomp(word1, "defprot") ||
490 0 == strcasecomp(word1, "protect"))
491 word3 = pointer; /* The rest of the line to be parsed by AA module */
492 else
493 word3 = HTNextField(&pointer); /* Just the next word */
494
495 if (!word2) {
496 fprintf(stderr, "HTRule: %s %s\n", RULE_NEEDS_DATA, line);
497 FREE(line);
498 return -2; /*syntax error */
499 }
500
501 if (0 == strcasecomp(word1, "suffix")) {
502 char *encoding = HTNextField(&pointer);
503
504 status = 0;
505 if (pointer)
506 status = LYscanFloat(pointer, &quality);
507
508 HTSetSuffix(word2, word3,
509 encoding ? encoding : "binary",
510 status >= 1 ? quality : (float) 1.0);
511
512 } else if (0 == strcasecomp(word1, "presentation")) {
513 status = 0;
514 if (pointer) {
515 const char *temp = pointer;
516
517 if (LYscanFloat2(&temp, &quality)) {
518 status = 1;
519 if (LYscanFloat2(&temp, &secs)) {
520 status = 2;
521 if (LYscanFloat2(&temp, &secs_per_byte)) {
522 status = 3;
523 if (sscanf(temp, "%ld", &maxbytes)) {
524 status = 4;
525 }
526 }
527 }
528 }
529 }
530
531 HTSetPresentation(word2, word3, NULL,
532 status >= 1 ? quality : 1.0,
533 status >= 2 ? secs : 0.0,
534 status >= 3 ? secs_per_byte : 0.0,
535 status >= 4 ? maxbytes : 0,
536 mediaCFG);
537
538 } else if (0 == strncasecomp(word1, "htbin", 5) ||
539 0 == strncasecomp(word1, "bindir", 6)) {
540 StrAllocCopy(HTBinDir, word2); /* Physical /htbin location */
541
542 } else if (0 == strncasecomp(word1, "search", 6)) {
543 StrAllocCopy(HTSearchScript, word2); /* Search script name */
544
545 } else {
546 op = 0 == strcasecomp(word1, "map") ? HT_Map
547 : 0 == strcasecomp(word1, "pass") ? HT_Pass
548 : 0 == strcasecomp(word1, "fail") ? HT_Fail
549 : 0 == strcasecomp(word1, "redirect") ? HT_Redirect
550 : 0 == strncasecomp(word1, "redirectperm", 12) ? HT_RedirectPerm
551 : 0 == strcasecomp(word1, "redirecttemp") ? HT_Redirect
552 : 0 == strcasecomp(word1, "permitredirection") ? HT_PermitRedir
553 : 0 == strcasecomp(word1, "useproxy") ? HT_UseProxy
554 : 0 == strcasecomp(word1, "alert") ? HT_Alert
555 : 0 == strcasecomp(word1, "alwaysalert") ? HT_AlwaysAlert
556 : 0 == strcasecomp(word1, "progress") ? HT_Progress
557 : 0 == strcasecomp(word1, "usermsg") ? HT_UserMsg
558 : 0 == strcasecomp(word1, "infomsg") ? HT_InfoMsg
559 : 0 == strcasecomp(word1, "defprot") ? HT_DefProt
560 : 0 == strcasecomp(word1, "protect") ? HT_Protect
561 : HT_Invalid;
562 if (op == HT_Invalid) {
563 fprintf(stderr, "HTRule: %s '%s'\n", RULE_INCORRECT, config);
564 } else {
565 switch (op) {
566 case HT_Fail: /* never a or other 2nd parameter */
567 case HT_PermitRedir:
568 cond_op = word3;
569 if (cond_op && *cond_op) {
570 word3 = NULL;
571 cond = HTNextField(&pointer);
572 }
573 break;
574
575 case HT_Pass: /* possibly a URL2 */
576 if (word3 && (!strcasecomp(word3, "if") ||
577 !strcasecomp(word3, "unless"))) {
578 cond_op = word3;
579 word3 = NULL;
580 cond = HTNextField(&pointer);
581 break;
582 }
583 /* else fall through */
584 case HT_Map: /* always a URL2 (or other 2nd parameter) */
585 case HT_Redirect:
586 case HT_RedirectPerm:
587 case HT_UseProxy:
588 cond_op = HTNextField(&pointer);
589 /* check for extra status word in "Redirect" */
590 if (op == HT_Redirect && 0 == strcasecomp(word1, "redirect") &&
591 cond_op &&
592 strcasecomp(cond_op, "if") &&
593 strcasecomp(cond_op, "unless")) {
594 if (0 == strcmp(word2, "301") ||
595 0 == strcasecomp(word2, "permanent")) {
596 op = HT_RedirectPerm;
597 } else if (!(0 == strcmp(word2, "302") ||
598 0 == strcmp(word2, "303") ||
599 0 == strcasecomp(word2, "temp") ||
600 0 == strcasecomp(word2, "seeother"))) {
601 CTRACE((tfp, "Rule: Ignoring `%s' in Redirect\n", word2));
602 }
603 word2 = word3;
604 word3 = cond_op; /* cond_op isn't condition op after all */
605 cond_op = HTNextField(&pointer);
606 }
607 if (cond_op && *cond_op)
608 cond = HTNextField(&pointer);
609 break;
610
611 case HT_Progress:
612 case HT_InfoMsg:
613 case HT_UserMsg:
614 case HT_Alert:
615 case HT_AlwaysAlert:
616 cond_op = HTNextField(&pointer);
617 if (cond_op && *cond_op)
618 cond = HTNextField(&pointer);
619 if (word3) { /* Fix string with too may %s - kw */
620 const char *cp = word3;
621 char *cp1, *cp2;
622
623 while ((cp1 = strchr(cp, '%'))) {
624 if (cp1[1] == '\0') {
625 *cp1 = '\0';
626 break;
627 } else if (cp1[1] == '%') {
628 cp = cp1 + 2;
629 continue;
630 } else
631 while ((cp2 = strchr(cp1 + 2, '%'))) {
632 if (cp2[1] == '\0') {
633 *cp2 = '\0';
634 break;
635 } else if (cp2[1] == '%') {
636 cp1 = cp2;
637 } else {
638 *cp2 = '?'; /* replace bad % */
639 cp1 = cp2;
640 }
641 }
642 break;
643 }
644 }
645 break;
646
647 default:
648 break;
649 }
650 if (cond_op && cond && *cond && !strcasecomp(cond_op, "unless")) {
651 cond_op = "unless";
652 } else if (cond_op && cond && *cond &&
653 !strcasecomp(cond_op, "if")) {
654 cond_op = "if";
655 } else if (cond_op || cond) {
656 fprintf(stderr, "HTRule: %s '%s'\n", RULE_INCORRECT, config);
657 FREE(line); /* syntax error, condition is a mess - kw */
658 return -2; /* NB unrecognized cond passes here - kw */
659 }
660 if (cond && !strncasecomp(cond, "redirected", (int) strlen(cond))) {
661 cond = "redirected"; /* recognized, canonical case - kw */
662 } else if (cond && strlen(cond) >= 8 &&
663 !strncasecomp(cond, "userspecified", (int) strlen(cond))) {
664 cond = "userspec"; /* also allow abbreviation - kw */
665 }
666 HTAddRule(op, word2, word3, cond_op, cond);
667 }
668 }
669 FREE(line);
670 return 0;
671 }
672
673 /* Load the rules from a file HTLoadRules()
674 * --------------------------
675 *
676 * On entry,
677 * Rules can be in any state
678 * On exit,
679 * Any existing rules will have been kept.
680 * Any new rules will have been loaded.
681 * Returns 0 if no error, 0 if error!
682 *
683 * Bugs:
684 * The strings may not contain spaces.
685 */
686
HTLoadRules(const char * filename)687 int HTLoadRules(const char *filename)
688 {
689 FILE *fp = fopen(filename, TXT_R);
690 char line[LINE_LENGTH + 1];
691
692 if (!fp) {
693 CTRACE((tfp, "HTRules: Can't open rules file %s\n", filename));
694 return -1; /* File open error */
695 }
696 for (;;) {
697 if (!fgets(line, LINE_LENGTH + 1, fp))
698 break; /* EOF or error */
699 (void) HTSetConfiguration(line);
700 }
701 fclose(fp);
702 return 0; /* No error or syntax errors ignored */
703 }
704
705 #endif /* NO_RULES */
706