Annotation of libwww/Library/src/HTAABrow.c, revision 2.46
2.15 frystyk 1: /* HTAABrow.c
2.32 frystyk 2: ** BROWSER SIDE ACCESS AUTHORIZATION MODULE
2.15 frystyk 3: **
2.19 frystyk 4: ** (c) COPYRIGHT MIT 1995.
2.15 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
2.46 ! frystyk 6: ** @(#) $Id: HTAABrow.c,v 2.45 1996/10/07 02:04:10 frystyk Exp $
2.1 luotonen 7: **
2.32 frystyk 8: ** Contains code for parsing challenges and creating credentials for
2.36 frystyk 9: ** basic authentication schemes. See also the HTAAUtil module
2.32 frystyk 10: ** for how to handle other authentication schemes. You don't have to use
11: ** this code at all.
2.1 luotonen 12: **
13: ** AUTHORS:
14: ** AL Ari Luotonen luotonen@dxcern.cern.ch
2.32 frystyk 15: ** HFN Henrik Frystyk
2.1 luotonen 16: **
17: ** HISTORY:
2.5 luotonen 18: ** Oct 17 AL Made corrections suggested by marca:
19: ** Added if (!realm->username) return NULL;
20: ** Changed some ""s to NULLs.
2.33 frystyk 21: ** Now doing HT_CALLOC() to init uuencode source;
2.5 luotonen 22: ** otherwise HTUU_encode() reads uninitialized memory
23: ** every now and then (not a real bug but not pretty).
24: ** Corrected the formula for uuencode destination size.
2.32 frystyk 25: ** Feb 96 HFN Rewritten to make it scheme independent and based on
26: ** callback functions and an info structure
2.1 luotonen 27: */
28:
2.17 frystyk 29: /* Library include files */
2.27 frystyk 30: #include "WWWLib.h"
31: #include "HTAAUtil.h"
32: #include "HTAABrow.h" /* Implemented here */
2.1 luotonen 33:
2.36 frystyk 34: #define BASIC_AUTH "basic"
2.44 frystyk 35: #define DIGEST_AUTH "digest"
2.36 frystyk 36:
2.32 frystyk 37: typedef struct _HTBasic { /* Basic challenge and credentials */
38: char * uid;
39: char * pw;
2.38 frystyk 40: BOOL retry; /* Should we ask the user again? */
2.40 frystyk 41: BOOL proxy; /* Proxy authentication */
2.32 frystyk 42: } HTBasic;
43:
2.44 frystyk 44: typedef struct _HTDigest { /* Digest challenge and credentials */
45: char * uid;
46: char * pw;
47: char * nounce;
48: char * opaque;
49: BOOL stale;
50: BOOL retry; /* Should we ask the user again? */
51: BOOL proxy; /* Proxy authentication */
52: int references; /* Number of pointers to this object */
53: } HTDigest;
54:
2.36 frystyk 55: /* ------------------------------------------------------------------------- */
56:
2.32 frystyk 57: /*
58: ** Create a protection template for the files
59: ** in the same directory as the given file
60: ** Returns a template matching docname, and other files in that directory.
61: **
62: ** E.g. /foo/bar/x.html => /foo/bar/ *
63: ** ^
64: ** Space only to prevent it from
65: ** being a comment marker here,
66: ** there really isn't any space.
2.1 luotonen 67: */
2.33 frystyk 68: PRIVATE char * make_template (const char * docname)
2.1 luotonen 69: {
2.39 frystyk 70: char * tmplate = NULL;
2.32 frystyk 71: if (docname) {
2.39 frystyk 72: char * host = HTParse(docname, "", PARSE_ACCESS|PARSE_HOST|PARSE_PUNCTUATION);
73: char * path = HTParse(docname, "", PARSE_PATH|PARSE_PUNCTUATION);
74: char * slash = strrchr(path, '/');
75: if (slash) {
76: if (*(slash+1)) {
77: strcpy(slash, "*");
78: StrAllocCat(host, path);
79: } else
2.43 frystyk 80: StrAllocCat(host, "/*");
2.39 frystyk 81: }
82: HT_FREE(path);
83: tmplate = host;
84: } else
85: StrAllocCopy(tmplate, "*");
2.32 frystyk 86: if (AUTH_TRACE)
87: HTTrace("Template.... Made template `%s' for file `%s'\n",
2.39 frystyk 88: tmplate, docname ? docname : "<null>");
2.32 frystyk 89: return tmplate;
2.1 luotonen 90: }
91:
2.44 frystyk 92: /* ------------------------------------------------------------------------- */
93: /* Basic Authentication */
94: /* ------------------------------------------------------------------------- */
95:
96: /*
97: ** Prompt the user for username and password.
98: ** Returns YES if user name was typed in, else NO
99: */
100: PRIVATE int prompt_user (HTRequest * request, const char * realm,
101: HTBasic * basic)
102: {
103: HTAlertCallback * cbf = HTAlert_find(HT_A_USER_PW);
104: if (request && cbf) {
105: HTAlertPar * reply = HTAlert_newReply();
106: int msg = basic->proxy ? HT_MSG_PROXY_UID : HT_MSG_UID;
107: BOOL res = (*cbf)(request, HT_A_USER_PW, msg,
108: basic->uid, (char *) realm, reply);
109: if (res) {
110: HT_FREE(basic->uid);
111: HT_FREE(basic->pw);
112: basic->uid = HTAlert_replyMessage(reply);
113: basic->pw = HTAlert_replySecret(reply);
114: }
115: HTAlert_deleteReply(reply);
116: return res ? HT_OK : HT_ERROR;
117: }
118: return HT_OK;
119: }
120:
121: PRIVATE HTBasic * HTBasic_new()
122: {
123: HTBasic * me = NULL;
124: if ((me = (HTBasic *) HT_CALLOC(1, sizeof(HTBasic))) == NULL)
125: HT_OUTOFMEM("HTBasic_new");
126: me->retry = YES; /* Ask the first time through */
127: return me;
128: }
129:
130: /* HTBasic_delete
131: ** --------------
132: ** Deletes a "basic" information object
133: */
134: PUBLIC int HTBasic_delete (void * context)
135: {
136: HTBasic * basic = (HTBasic *) context;
137: if (basic) {
138: HT_FREE(basic->uid);
139: HT_FREE(basic->pw);
140: HT_FREE(basic);
141: return YES;
142: }
143: return NO;
144: }
145:
2.32 frystyk 146: /*
147: ** Make basic authentication scheme credentials and register this
148: ** information in the request object as credentials. They will then
149: ** be included in the request header. An example is
150: **
151: ** "Basic AkRDIhEF8sdEgs72F73bfaS=="
152: **
2.40 frystyk 153: ** The function can both create normal and proxy credentials
2.36 frystyk 154: ** Returns HT_OK or HT_ERROR
2.32 frystyk 155: */
2.36 frystyk 156: PRIVATE BOOL basic_credentials (HTRequest * request, HTBasic * basic)
2.32 frystyk 157: {
158: if (request && basic) {
159: char * cleartext = NULL;
160: char * cipher = NULL;
161: int cl_len = strlen(basic->uid ? basic->uid : "") +
2.42 frystyk 162: strlen(basic->pw ? basic->pw : "") + 5;
2.37 frystyk 163: int ci_len = 4 * (((cl_len+2)/3) + 1);
164: if ((cleartext = (char *) HT_CALLOC(1, cl_len)) == NULL)
2.32 frystyk 165: HT_OUTOFMEM("basic_credentials");
166: *cleartext = '\0';
167: if (basic->uid) strcpy(cleartext, basic->uid);
168: strcat(cleartext, ":");
169: if (basic->pw) strcat(cleartext, basic->pw);
2.37 frystyk 170: if ((cipher = (char *) HT_CALLOC(1, ci_len + 3)) == NULL)
2.32 frystyk 171: HT_OUTOFMEM("basic_credentials");
2.37 frystyk 172: HTUU_encode((unsigned char *) cleartext, strlen(cleartext), cipher);
2.1 luotonen 173:
2.32 frystyk 174: /* Create the credentials and assign them to the request object */
175: {
2.37 frystyk 176: int cr_len = strlen("basic") + ci_len + 3;
2.32 frystyk 177: char * cookie = (char *) HT_MALLOC(cr_len+1);
178: if (!cookie) HT_OUTOFMEM("basic_credentials");
179: strcpy(cookie, "Basic ");
180: strcat(cookie, cipher);
2.37 frystyk 181: if (AUTH_TRACE) HTTrace("Basic Cookie `%s\'\n", cookie);
2.40 frystyk 182:
183: /* Check whether it is proxy or normal credentials */
184: if (basic->proxy)
185: HTRequest_addCredentials(request, "Proxy-Authorization", cookie);
186: else
187: HTRequest_addCredentials(request, "Authorization", cookie);
188:
2.32 frystyk 189: HT_FREE(cookie);
2.1 luotonen 190: }
2.32 frystyk 191: HT_FREE(cleartext);
192: HT_FREE(cipher);
2.36 frystyk 193: return HT_OK;
2.32 frystyk 194: }
2.36 frystyk 195: return HT_ERROR;
2.1 luotonen 196: }
197:
2.32 frystyk 198: /* HTBasic_generate
199: ** ----------------
200: ** This function generates "basic" credentials for the challenge found in
201: ** the authentication information base for this request. The result is
202: ** stored as an association list in the request object.
203: ** This is a callback function for the AA handler.
204: */
2.45 frystyk 205: PUBLIC int HTBasic_generate (HTRequest * request, void * context, int mode)
2.32 frystyk 206: {
2.36 frystyk 207: HTBasic * basic = (HTBasic *) context;
2.45 frystyk 208: BOOL proxy = mode==HT_NO_PROXY_ACCESS ? YES : NO;
2.36 frystyk 209: if (request) {
210: const char * realm = HTRequest_realm(request);
211:
2.40 frystyk 212: /*
2.46 ! frystyk 213: ** If we were asked to explicitly ask the user again
! 214: */
! 215: if (mode == HT_REAUTH || mode == HT_PROXY_REAUTH)
! 216: basic->retry = YES;
! 217:
! 218: /*
2.40 frystyk 219: ** If we don't have a basic context then add a new one to the tree.
2.42 frystyk 220: ** We use different trees for normal and proxy authentication
2.40 frystyk 221: */
2.36 frystyk 222: if (!basic) {
2.42 frystyk 223: if (proxy) {
224: char * url = HTRequest_proxy(request);
225: basic = HTBasic_new();
226: basic->proxy = YES;
227: HTAA_updateNode(proxy, BASIC_AUTH, realm, url, basic);
228: } else {
229: char * url = HTAnchor_address((HTAnchor*)HTRequest_anchor(request));
230: basic = HTBasic_new();
231: HTAA_updateNode(proxy, BASIC_AUTH, realm, url, basic);
232: HT_FREE(url);
233: }
2.36 frystyk 234: }
235:
2.32 frystyk 236: /*
2.36 frystyk 237: ** If we have a set of credentials (or the user provides a new set)
238: ** then store it in the request object as the credentials
2.32 frystyk 239: */
2.39 frystyk 240: if ((basic->retry && prompt_user(request, realm, basic) == HT_OK) ||
241: (!basic->retry && basic->uid)) {
2.38 frystyk 242: basic->retry = NO;
2.36 frystyk 243: return basic_credentials(request, basic);
2.38 frystyk 244: } else
2.37 frystyk 245: return HT_ERROR;
2.1 luotonen 246: }
2.36 frystyk 247: return HT_OK;
2.1 luotonen 248: }
249:
2.32 frystyk 250: /* HTBasic_parse
251: ** -------------
252: ** This function parses the contents of a "basic" challenge
253: ** and stores the challenge in our authentication information datebase.
254: ** We also store the realm in the request object which will help finding
255: ** the right set of credentials to generate.
256: ** The function is a callback function for the AA handler.
257: */
2.45 frystyk 258: PUBLIC int HTBasic_parse (HTRequest * request, HTResponse * response,
259: void * context, int status)
2.32 frystyk 260: {
2.45 frystyk 261: HTAssocList * challenge = HTResponse_challenge(response);
2.38 frystyk 262: HTBasic * basic = NULL;
2.40 frystyk 263: BOOL proxy = status==HT_NO_PROXY_ACCESS ? YES : NO;
2.36 frystyk 264: if (request && challenge) {
265: char * p = HTAssocList_findObject(challenge, BASIC_AUTH);
266: char * realm = HTNextField(&p);
267: char * rm = HTNextField(&p);
2.38 frystyk 268:
2.32 frystyk 269: /*
2.36 frystyk 270: ** If valid challenge then make a template for the resource and
271: ** store this information in our authentication URL Tree
2.32 frystyk 272: */
2.36 frystyk 273: if (realm && !strcasecomp(realm, "realm") && rm) {
274: if (AUTH_TRACE) HTTrace("Basic Parse. Realm `%s\' found\n", rm);
275: HTRequest_setRealm(request, rm);
2.40 frystyk 276:
277: /*
278: ** If we are in proxy mode then add the proxy - not the final URL
279: */
280: if (proxy) {
281: char * url = HTRequest_proxy(request);
2.42 frystyk 282: if (AUTH_TRACE) HTTrace("Basic Parse. Proxy authentication\n");
2.40 frystyk 283: basic = (HTBasic *) HTAA_updateNode(proxy, BASIC_AUTH, rm,
284: url, NULL);
285: } else {
286: char * url = HTAnchor_address((HTAnchor *)
287: HTRequest_anchor(request));
288: char * tmplate = make_template(url);
289: basic = (HTBasic *) HTAA_updateNode(proxy, BASIC_AUTH, rm,
290: tmplate, NULL);
291: HT_FREE(url);
292: HT_FREE(tmplate);
293: }
2.1 luotonen 294: }
2.38 frystyk 295:
296: /*
297: ** For some reason the authentication failed so we have to ask the user
298: ** if we should try again. It may be because the user typed the wrong
299: ** user name and password
300: */
301: if (basic) {
302: HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM);
2.40 frystyk 303:
304: /*
2.42 frystyk 305: ** Do we haev a method registered for prompting the user whether
306: ** we should retry
2.40 frystyk 307: */
2.38 frystyk 308: if (prompt) {
2.40 frystyk 309: int code = proxy ?
310: HT_MSG_RETRY_PROXY_AUTH : HT_MSG_RETRY_AUTHENTICATION;
311: if ((*prompt)(request, HT_A_CONFIRM, code,
2.38 frystyk 312: NULL, NULL, NULL) != YES)
313: return HT_ERROR;
314: basic->retry = YES;
315: }
316: }
2.36 frystyk 317: return HT_OK;
2.1 luotonen 318: }
2.36 frystyk 319: if (AUTH_TRACE) HTTrace("Auth........ No challenges found\n");
2.38 frystyk 320: return HT_ERROR;
2.7 luotonen 321: }
2.44 frystyk 322:
323: /* ------------------------------------------------------------------------- */
324: /* Digest Authentication */
325: /* ------------------------------------------------------------------------- */
326:
327: /*
328: ** Prompt the user for username and password.
329: ** Returns YES if user name was typed in, else NO
330: */
331: PRIVATE int prompt_digest_user (HTRequest * request, const char * realm,
332: HTDigest * digest)
333: {
334: HTAlertCallback * cbf = HTAlert_find(HT_A_USER_PW);
335: if (request && cbf) {
336: HTAlertPar * reply = HTAlert_newReply();
337: int msg = digest->proxy ? HT_MSG_PROXY_UID : HT_MSG_UID;
338: BOOL res = (*cbf)(request, HT_A_USER_PW, msg,
339: digest->uid, (char *) realm, reply);
340: if (res) {
341: HT_FREE(digest->uid);
342: HT_FREE(digest->pw);
343: digest->uid = HTAlert_replyMessage(reply);
344: digest->pw = HTAlert_replySecret(reply);
345: }
346: HTAlert_deleteReply(reply);
347: return res ? HT_OK : HT_ERROR;
348: }
349: return HT_OK;
350: }
351:
352: PRIVATE HTDigest * HTDigest_new()
353: {
354: HTDigest * me = NULL;
355: if ((me = (HTDigest *) HT_CALLOC(1, sizeof(HTDigest))) == NULL)
356: HT_OUTOFMEM("HTDigest_new");
357: me->retry = YES; /* Ask the first time through */
358: return me;
359: }
360:
361: /* HTDigest_delete
362: ** --------------
363: ** Deletes a "digest" information object
364: ** A single object may be registered multiple places in the URL tree.
365: ** We keep a simple reference count on the object so that we know
366: ** when to delete the object.
367: */
368: PUBLIC int HTDigest_delete (void * context)
369: {
370: HTDigest * digest = (HTDigest *) context;
371: if (digest) {
372: if (digest->references <= 0) {
373: HT_FREE(digest->uid);
374: HT_FREE(digest->pw);
375: HT_FREE(digest->nounce);
376: HT_FREE(digest->opaque);
377: HT_FREE(digest);
378: } else
379: digest->references--;
380: return YES;
381: }
382: return NO;
383: }
384:
385: /*
386: ** Make digest authentication scheme credentials and register this
387: ** information in the request object as credentials. They will then
388: ** be included in the request header.
389: ** The function can both create normal and proxy credentials
390: ** Returns HT_OK or HT_ERROR
391: */
392: PRIVATE BOOL digest_credentials (HTRequest * request, HTDigest * digest)
393: {
394: if (request && digest) {
395:
396: /* THIS IS CURRENTLY FOR BASIC AUTH. CHANGE THIS TO DIGEST */
397:
398: char * cleartext = NULL;
399: char * cipher = NULL;
400: int cl_len = strlen(digest->uid ? digest->uid : "") +
401: strlen(digest->pw ? digest->pw : "") + 5;
402: int ci_len = 4 * (((cl_len+2)/3) + 1);
403: if ((cleartext = (char *) HT_CALLOC(1, cl_len)) == NULL)
404: HT_OUTOFMEM("digest_credentials");
405: *cleartext = '\0';
406: if (digest->uid) strcpy(cleartext, digest->uid);
407: strcat(cleartext, ":");
408: if (digest->pw) strcat(cleartext, digest->pw);
409: if ((cipher = (char *) HT_CALLOC(1, ci_len + 3)) == NULL)
410: HT_OUTOFMEM("digest_credentials");
411: HTUU_encode((unsigned char *) cleartext, strlen(cleartext), cipher);
412:
413: /* Create the credentials and assign them to the request object */
414: {
415: int cr_len = strlen("digest") + ci_len + 3;
416: char * cookie = (char *) HT_MALLOC(cr_len+1);
417: if (!cookie) HT_OUTOFMEM("digest_credentials");
418: strcpy(cookie, "Digest ");
419: strcat(cookie, cipher);
420: if (AUTH_TRACE) HTTrace("Digest Cookie `%s\'\n", cookie);
421:
422: /* Check whether it is proxy or normal credentials */
423: if (digest->proxy)
424: HTRequest_addCredentials(request, "Proxy-Authorization", cookie);
425: else
426: HTRequest_addCredentials(request, "Authorization", cookie);
427:
428: HT_FREE(cookie);
429: }
430: HT_FREE(cleartext);
431: HT_FREE(cipher);
432: return HT_OK;
433: }
434: return HT_ERROR;
435: }
436:
437: /* HTDigest_generate
438: ** ----------------
439: ** This function generates "digest" credentials for the challenge found in
440: ** the authentication information base for this request. The result is
441: ** stored as an association list in the request object.
442: ** This is a callback function for the AA handler.
443: */
2.45 frystyk 444: PUBLIC int HTDigest_generate (HTRequest * request, void * context, int mode)
2.44 frystyk 445: {
446: HTDigest * digest = (HTDigest *) context;
2.45 frystyk 447: BOOL proxy = mode==HT_NO_PROXY_ACCESS ? YES : NO;
2.44 frystyk 448: if (request) {
449: const char * realm = HTRequest_realm(request);
2.46 ! frystyk 450:
! 451: /*
! 452: ** If we were asked to explicitly ask the user again
! 453: */
! 454: if (mode == HT_REAUTH || mode == HT_PROXY_REAUTH)
! 455: digest->retry = YES;
2.44 frystyk 456:
457: /*
458: ** If we don't have a digest context then add a new one to the tree.
459: ** We use different trees for normal and proxy authentication
460: */
461: if (!digest) {
462: if (proxy) {
463: char * url = HTRequest_proxy(request);
464: digest = HTDigest_new();
465: digest->proxy = YES;
466: HTAA_updateNode(proxy, DIGEST_AUTH, realm, url, digest);
467: } else {
468: char * url = HTAnchor_address((HTAnchor*)HTRequest_anchor(request));
469: digest = HTDigest_new();
470: HTAA_updateNode(proxy, DIGEST_AUTH, realm, url, digest);
471: HT_FREE(url);
472: }
473: }
474:
475: /*
476: ** If we have a set of credentials (or the user provides a new set)
477: ** then store it in the request object as the credentials
478: */
479: if ((digest->retry &&
480: prompt_digest_user(request, realm, digest) == HT_OK) ||
481: (!digest->retry && digest->uid)) {
482: digest->retry = NO;
483: return digest_credentials(request, digest);
484: } else
485: return HT_ERROR;
486: }
487: return HT_OK;
488: }
489:
490: /* HTDigest_parse
491: ** -------------
492: ** This function parses the contents of a "digest" challenge
493: ** and stores the challenge in our authentication information datebase.
494: ** We also store the realm in the request object which will help finding
495: ** the right set of credentials to generate.
496: ** The function is a callback function for the AA handler.
497: */
2.45 frystyk 498: PUBLIC int HTDigest_parse (HTRequest * request, HTResponse * response,
499: void * context, int status)
2.44 frystyk 500: {
2.45 frystyk 501: HTAssocList * challenge = HTResponse_challenge(response);
2.44 frystyk 502: HTDigest * digest = NULL;
503: BOOL proxy = status==HT_NO_PROXY_ACCESS ? YES : NO;
504: if (request && challenge) {
505: char * p = HTAssocList_findObject(challenge, DIGEST_AUTH);
506: char * realm = HTNextField(&p);
507: char * value = HTNextField(&p);
508: char * token = NULL;
509: char * uris = NULL;
510: BOOL found = NO;
511:
512: /*
513: ** Search for the realm and see if we have an entry for it. If not
514: ** then create a new entry.
515: */
516: if (realm && !strcasecomp(realm, "realm") && value) {
517: if (AUTH_TRACE) HTTrace("Basic Parse. Realm `%s\' found\n", value);
518: HTRequest_setRealm(request, value);
519: digest = (HTDigest *)
520: HTAA_updateNode(proxy, DIGEST_AUTH, value, NULL, NULL);
521: }
522: if (!digest)
523: digest = HTDigest_new();
524: else
525: found = YES;
526:
527: /*
528: ** Search through the set of parameters in the digest header.
529: ** If valid challenge then make a template for the resource and
530: ** store this information in our authentication URL Tree
531: */
532: while ((token = HTNextField(&p))) {
533: if (!strcasecomp(token, "domain")) {
534: if ((value = HTNextField(&p)))
535: uris = value;
536: } else if (!strcasecomp(token, "nounce")) {
537: if ((value = HTNextField(&p)))
538: StrAllocCopy(digest->nounce, value);
539: } else if (!strcasecomp(token, "opaque")) {
540: if ((value = HTNextField(&p)))
541: StrAllocCopy(digest->opaque, value);
542: } else if (!strcasecomp(token, "stale")) {
543: if ((value = HTNextField(&p)) && !strcasecomp(value, "true"))
544: digest->stale = YES;
545: } else if (!strcasecomp(token, "algorithm")) {
546: if ((value = HTNextField(&p)) && strcasecomp(value, "md5")) {
547: /*
548: ** We only support MD5 for the moment
549: */
550: if (AUTH_TRACE) HTTrace("Digest Parse Unknown algorithm `%s\'\n", value);
551: HTDigest_delete(digest);
552: return HT_ERROR;
553: }
554: }
555: }
556:
557: /*
558: ** Now as we have parsed the full digest header we update the URL tree
559: ** with the new information. If we didn't get a "domain" token then
560: ** add the node using a template. If we have multiple URIs in the
561: ** domain token then add a digest node at each URI.
562: */
563: if (!uris) {
564: if (proxy) {
565: char * location = HTRequest_proxy(request);
566: if (AUTH_TRACE) HTTrace("Digest Parse Proxy authentication\n");
567: HTAA_updateNode(proxy, DIGEST_AUTH, realm, location, digest);
568: } else {
569: char * url = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
570: char * tmplate = make_template(url);
571: HTAA_updateNode(proxy, DIGEST_AUTH, realm, tmplate, digest);
572: HT_FREE(url);
573: HT_FREE(tmplate);
574: }
575: } else {
576:
577: /*
578: ** ADD THE DIGEST FOR EACH URL IN THE LIST AND INCREMENT THE
579: ** REFERENCE COUNT IN THE DIGEST BY ONE
580: */
581:
582: }
583:
584: /*
585: ** For some reason the authentication failed so we have to ask the user
586: ** if we should try again. It may be because the user typed the wrong
587: ** user name and password
588: */
589: if (found) {
590: HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM);
591:
592: /*
593: ** Do we have a method registered for prompting the user whether
594: ** we should retry
595: */
596: if (prompt) {
597: int code = proxy ?
598: HT_MSG_RETRY_PROXY_AUTH : HT_MSG_RETRY_AUTHENTICATION;
599: if ((*prompt)(request, HT_A_CONFIRM, code,
600: NULL, NULL, NULL) != YES)
601: return HT_ERROR;
602: digest->retry = YES;
603: }
604: }
605: return HT_OK;
606: }
607: if (AUTH_TRACE) HTTrace("Auth........ No challenges found\n");
608: return HT_ERROR;
609: }
610:
Webmaster